From: Hanlyu Sarang Date: 2016-02-22T10:50:17-06:00 Subject: [ruby-core:73927] Re: [Ruby trunk Feature#11262] Make more objects behave like "Functions" --===============0429241500== Content-Type: multipart/alternative; boundary=089e015374d8a85904052c5ea042 --089e015374d8a85904052c5ea042 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable This is one of the few cases where JavaScript is superior to ruby. You can have just about anything and append parentheses to it and presto, an invocation will be attempted "just_about_anything(...)". JavaScript's approach is both powerful and clean. Ruby's approach is also powerful, but it is complicated. I usually choose between "->myarg{...}" or "myobj.method :mymethod". I also have to use ".call(...), a complaint which is really nitpicking. [If I love JavaScript so much, why don't I just use JavaScript instead of Ruby?] But I'm also comparing Apples and Oranges, which isn't fair. JavaScript doesn't have Blocks, nor does it have a compact poetry mode. I don't think there is much that is practical which can be done to clean up Ruby function calling syntax. I appreciate stabby, which is really very nice. On Mon, Feb 22, 2016 at 9:27 AM, wrote: > Issue #11262 has been updated by J=C3=B6rg W Mittag. > > Project changed from CommonRuby to Ruby trunk > Description updated > > ---------------------------------------- > Feature #11262: Make more objects behave like "Functions" > https://bugs.ruby-lang.org/issues/11262#change-57075 > > * Author: J=C3=B6rg W Mittag > * Status: Open > * Priority: Normal > * Assignee: > ---------------------------------------- > # What is a Function? > > In Ruby, we have the [`Proc`](http://ruby-doc.org/core/Proc.html) class > to represent objects which are "function-like". But, in true > object-oriented / duck-typing fashion, an object doesn't actually have to > be an instance of `Proc` in order to be treated as a function, it only > needs to respond to [`call`]( > http://ruby-doc.org/core/Proc.html#method-i-call). For cases, where a > `Proc` instance is absolutely required (mostly, the `&` unary prefix > ampersand "make-me-a-block" operator), there is the [`to_proc`]( > http://ruby-doc.org/core/Proc.html#method-i-to_proc) conversion. > > So, in short: if an object wants to be a function, it **MUST** respond to > `call`, and **SHOULD** also respond to `to_proc`. > > There are some objects in Ruby that *could* be seen as functions, but > currently don't respond to `call` or `to_proc`: > > # [`Array`](http://ruby-doc.org/core/Array.html) as mapping > > An array is a mapping from indices to elements. "Mapping" is just a > different word for (partial) function, though! I propose, that `Array` > should implement `call` and `to_proc` in the following manner: > > ~~~ruby > class Array > alias_method :call, :[] > > def to_proc > method(:call).to_proc > end > end > ~~~ > > # [`Hash`](http://ruby-doc.org/core/Hash.html) as mapping > > A hash is a mapping from keys to values. I propose, that `Hash` should > implement `call` and `to_proc` in the following manner: > > ~~~ruby > class Hash > alias_method :call, :[] > > def to_proc > method(:call).to_proc > end > end > ~~~ > > # `Set` as predicate > > A set is a mapping from values to booleans, i.e. a set is the same as its > `include?` predicate. This would mean, for example, that I can pass a `Se= t` > as a predicate to methods like [`Enumerable#select`]( > http://ruby-doc.org/core/Enumerable.html#method-i-select). I propose, > that `Set` should implement `call` and `to_proc` in the following manner: > > ~~~ruby > require 'set' > > class Set > alias_method :call, :include? > > def to_proc > method(:call).to_proc > end > end > ~~~ > > I believe that these three additions are worthwhile and fairly > uncontroversial. They match with the way arrays, maps and especially sets > are treated in mathematics and in other programming languages. E.g. in bo= th > [Clojure]( > http://clojure.org/data_structures#Data%20Structures-Maps%20(IPersistentM= ap)) > and [Scala](http://scala-lang.org/api/current/#scala.collection.Seq), > arrays, sets and maps are functions and use function application syntax f= or > accessing values. Scala doesn't even have indexing syntax. > > Here are some potential use cases: > > ~~~ruby > numbers_to_words =3D %w[zero one two three four five six seven eight nine > ten eleven twelve] > > [4, 7, 1, 0, 8].map(&numbers_to_words) > # =3D> ['four', 'seven', 'one', 'zero', 'eight'] > > > allowed_languages =3D Set[:ruby, :python, :scala, :scheme] > > %i[ruby c cplusplus scala java perl].select(&allowed_languages) > # =3D> [:ruby, :scala] > ~~~ > > Here is a more "wild" proposal that is much more controversial. I don't > actually propose adding this to Ruby, but I will mention it here as food > for thought: > > # [`Class`](http://ruby-doc.org/core/Class.html) as factory > > If you squint your eyes, tilt your head sideways and look at it juuuuuuus= t > right, a class is a factory for objects. In other words, it is a function > from constructor arguments to instances: > > ~~~ruby > class Class > alias_method :call, :new > > def to_proc > method(:call).to_proc > end > end > ~~~ > > Example: > > ~~~ruby > class Person > def initialize(name) > @name =3D name > end > end > > %w[matz ko1 charlie].map(&Person) > # =3D> [#, # @name=3D"ko1">, #] > ~~~ > > # Incompatibilities > > This proposal conflicts with #10829, which proposes to use `Array#to_proc= ` > for a completely different purpose. > > I believe that having `Array`s behave as functions from indices to > elements is natural, unsurprising, and well in line with both mathematics > and other languages. > > --- > > # Related > > The code duplication encountered here suggests refactoring to extract two > new mixins in the Ruby core library: > > ~~~ruby > module Callable > def to_proc > method(:call).to_proc > end > end > > module Indexable > alias_method :call, :[] > end > ~~~ > > However, this is out of scope of this discussion and *not* part of this > particular feature proposal. > > --- > > [NOTE: I originally posted this in project:common-ruby, which according t= o > [[common-ruby:|its wiki]] is "The official place to submit feature propos= al > for Ruby" but from my observation, almost all Ruby feature requests > actually get filed at project:ruby-trunk.] > > > > -- > https://bugs.ruby-lang.org/ > > Unsubscribe: > > --089e015374d8a85904052c5ea042 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
This is one of the few cases where JavaScript is superior = to ruby. You can have just about anything and append parentheses to it and = presto, an invocation will be attempted "just_about_anything(...)"= ;. JavaScript's approach is both powerful and clean. Ruby's approac= h is also powerful, but it is complicated. I usually choose between "-= >myarg{...}" or "myobj.method :mymethod". I also have to = use ".call(...), a complaint which is really nitpicking. [If I love Ja= vaScript so much, why don't I just use JavaScript instead of Ruby?]
=
But I'm also comparing Apples and Oranges, which isn't fair. Ja= vaScript doesn't have Blocks, nor does it have a compact poetry mode. I= don't think there is much that is practical which can be done to clean= up Ruby function calling syntax. I appreciate stabby, which is really very= nice.


On Mon, Feb 22, 2016 at 9:27 AM, <Ruby-Lang@joer= gwmittag.de> wrote:
Issue #= 11262 has been updated by J=C3=B6rg W Mittag.

Project changed from CommonRuby to Ruby trunk
Description updated

----------------------------------------
Feature #11262: Make more objects behave like "Functions"
https://bugs.ruby-lang.org/issues/11262#change-5= 7075

* Author: J=C3=B6rg W Mittag
* Status: Open
* Priority: Normal
* Assignee:
----------------------------------------
# What is a Function?

In Ruby, we have the [`Proc`](http://ruby-doc.org/core/Proc.html) class to represent objects which are "function-like". But, in= true object-oriented / duck-typing fashion, an object doesn't actually= have to be an instance of `Proc` in order to be treated as a function, it = only needs to respond to [`call`](http://ruby-doc.or= g/core/Proc.html#method-i-call). For cases, where a `Proc` instance is = absolutely required (mostly, the `&` unary prefix ampersand "make-= me-a-block" operator), there is the [`to_proc`](http://ruby-doc.org/core/Proc.html#method-i-to_proc) conversion.
So, in short: if an object wants to be a function, it **MUST** respond to `= call`, and **SHOULD** also respond to `to_proc`.

There are some objects in Ruby that *could* be seen as functions, but curre= ntly don't respond to `call` or `to_proc`:

# [`Array`](http://ruby-doc.org/core/Array.html) as mapping
An array is a mapping from indices to elements. "Mapping" is just= a different word for (partial) function, though! I propose, that `Array` s= hould implement `call` and `to_proc` in the following manner:

~~~ruby
class Array
=C2=A0 alias_method :call, :[]

=C2=A0 def to_proc
=C2=A0 =C2=A0 method(:call).to_proc
=C2=A0 end
end
~~~

# [`Hash`](http://ruby-doc.org/core/Hash.html) as mapping

A hash is a mapping from keys to values. I propose, that `Hash` should impl= ement `call` and `to_proc` in the following manner:

~~~ruby
class Hash
=C2=A0 alias_method :call, :[]

=C2=A0 def to_proc
=C2=A0 =C2=A0 method(:call).to_proc
=C2=A0 end
end
~~~

# `Set` as predicate

A set is a mapping from values to booleans, i.e. a set is the same as its `= include?` predicate. This would mean, for example, that I can pass a `Set` = as a predicate to methods like [`Enumerable#select`](http://ruby-doc.org/core/Enumerable.html#method-i-select). I p= ropose, that `Set` should implement `call` and `to_proc` in the following m= anner:

~~~ruby
require 'set'

class Set
=C2=A0 alias_method :call, :include?

=C2=A0 def to_proc
=C2=A0 =C2=A0 method(:call).to_proc
=C2=A0 end
end
~~~

I believe that these three additions are worthwhile and fairly uncontrovers= ial. They match with the way arrays, maps and especially sets are treated i= n mathematics and in other programming languages. E.g. in both [Clojure](http://clojure.org/data_st= ructures#Data%20Structures-Maps%20(IPersistentMap)) and [Scala](http://scala-lang.org/api/current/#scala.collection.= Seq), arrays, sets and maps are functions and use function application = syntax for accessing values. Scala doesn't even have indexing syntax.
Here are some potential use cases:

~~~ruby
numbers_to_words =3D %w[zero one two three four five six seven eight nine t= en eleven twelve]

[4, 7, 1, 0, 8].map(&numbers_to_words)
# =3D> ['four', 'seven', 'one', 'zero', = 'eight']


allowed_languages =3D Set[:ruby, :python, :scala, :scheme]

%i[ruby c cplusplus scala java perl].select(&allowed_languages)
# =3D> [:ruby, :scala]
~~~

Here is a more "wild" proposal that is much more controversial. I= don't actually propose adding this to Ruby, but I will mention it here= as food for thought:

# [`Class`](http://ruby-doc.org/core/Class.html) as factory
If you squint your eyes, tilt your head sideways and look at it juuuuuuust = right, a class is a factory for objects. In other words, it is a function f= rom constructor arguments to instances:

~~~ruby
class Class
=C2=A0 alias_method :call, :new

=C2=A0 def to_proc
=C2=A0 =C2=A0 method(:call).to_proc
=C2=A0 end
end
~~~

Example:

~~~ruby
class Person
=C2=A0 def initialize(name)
=C2=A0 =C2=A0 @name =3D name
=C2=A0 end
end

%w[matz ko1 charlie].map(&Person)
# =3D> [#<Person:0xdeadbeef481523 @name=3D"matz">, #<= Person:0xdeadbeef815234 @name=3D"ko1">, #<Person:0xdeadbeef= 152342 @name=3D"charlie">]
~~~

# Incompatibilities

This proposal conflicts with #10829, which proposes to use `Array#to_proc` = for a completely different purpose.

I believe that having `Array`s behave as functions from indices to elements= is natural, unsurprising, and well in line with both mathematics and other= languages.

---

# Related

The code duplication encountered here suggests refactoring to extract two n= ew mixins in the Ruby core library:

~~~ruby
module Callable
=C2=A0 def to_proc
=C2=A0 =C2=A0 method(:call).to_proc
=C2=A0 end
end

module Indexable
=C2=A0 alias_method :call, :[]
end
~~~

However, this is out of scope of this discussion and *not* part of this par= ticular feature proposal.

---

[NOTE: I originally posted this in project:common-ruby, which according to = [[common-ruby:|its wiki]] is "The official place to submit feature pro= posal for Ruby" but from my observation, almost all Ruby feature reque= sts actually get filed at project:ruby-trunk.]



--
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:= ruby-core-request@ruby-lang.org?subject=3Dunsubscribe>
<http://lists.ruby-lang.org/cgi-bin/m= ailman/options/ruby-core>

--089e015374d8a85904052c5ea042-- --===============0429241500== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline Unsubscribe: --===============0429241500==--