New magical version of Symbol.to_proc

[Posted at
http://drnicwilliams.com/2006/09/28/new-magical-version-of-symbolto_proc/\]

Before the magic, let’s go through a Beginner’s Guide to Mapping, then
Advanced Guide to Symbol.to_proc, and THEN, the magical version. Its
worth it. Its sexy, nifty AND magical all at once.

* Beginner’s Guide to Mapping

list = [’1′, ‘2′, ‘3′]

=> ["1″, "2″, "3″]

list.map {|item| item.to_i}

=> [1, 2, 3]

Here we're invoking to_i on each item of the list and returning the
result into a new list. That's map/collect for you.

* Advanced Guide to Symbol.to_proc

After doing that a few times, you start wishing there was simpler
syntax. Enter: Symbol.to_proc

list.map &:to_i

=> [1, 2, 3]

It works. Just enjoy it. (see article for links on why it works)

* Magical version of Symbol.to_proc

Quite frankly, that’s still a lot of syntax. Plus, I normally forget to
added parentheses around the &:to_i, and then latter I want to invoke
another method on the result, so I need to add the parentheses which is
a pain… anyway. I thought of something niftier and dare I say, more
magical.

How about this syntax:

list.to_is

=> [1, 2, 3]

By passing the plural version of a method, the array automagically
performs the above mapping on itself using the singular version of the
method.

Sexy! And here's more examples:

(1..10).to_a.to_ss

=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]

(1..10).to_a.days

=> [86400, 172800, 259200, 345600, 432000, 518400, 604800, 691200,
777600, 864000]

[2,'two', :two].classes

=> [Fixnum, String, Symbol]

[2,'two', :two].classes.names

=> ["Fixnum", "String", "Symbol"]

[2,'two', :two].classes.names.lengths

=> [6, 6, 6]

So much happy syntax in one place!

I've got the library that gives you this syntax here:
http://drnicwilliams.com/2006/09/28/new-magical-version-of-symbolto_proc/
(at the bottom)

Nic

···

--
Posted via http://www.ruby-forum.com/\.

Note: the Symbol.to_proc is currently in the Rails libraries
(active_support gem) but will be added into Ruby 1.9 one day in the
future.

···

--
Posted via http://www.ruby-forum.com/.

Hi --

list.map &:to_i

=> [1, 2, 3]

It works. Just enjoy it. (see article for links on why it works)

It's not my favorite construct; it's a bit ugly and terse in a bad
way. You mentioned elsewhere that this is going to be in 1.9, though
I remember Matz rejecting an RCR for:

   list.map(:to_i)

and &:to_i seems worse :slight_smile:

(1..10).to_a.to_ss

=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]

(1..10).to_a.days

=> [86400, 172800, 259200, 345600, 432000, 518400, 604800, 691200,
777600, 864000]

[2,'two', :two].classes

=> [Fixnum, String, Symbol]

[2,'two', :two].classes.names

=> ["Fixnum", "String", "Symbol"]

[2,'two', :two].classes.names.lengths

=> [6, 6, 6]

So much happy syntax in one place!

Actually the reason this is kind of cool is not the syntax but the
semantics. It's much more expressive than any number of &:->()...
things that have been proposed -- and looks better. (There may be
some connection there too.)

I wonder, though, whether it would be too easy for it to trample on
other method names. I guess that's true of anything -- you'd just
have to make sure you didn't have two conceptions of some plural
thing.

David

···

On Thu, 28 Sep 2006, Dr Nic wrote:

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Actually the reason this is kind of cool is not the syntax but the
semantics. It's much more expressive than any number of &:->()...
things that have been proposed -- and looks better. (There may be
some connection there too.)

Agreed - its very nice semantically too. It has much more meaning than
.map(&:method_name) I think.

I wonder, though, whether it would be too easy for it to trample on
other method names. I guess that's true of anything -- you'd just
have to make sure you didn't have two conceptions of some plural
thing.

The method_missing code will perform "super" first to ensure that any
per-existing dynamic methods on the Array are evaluated first. Then it
attempts to call the method on the collection items. If they return a
NoMethodError then the original error object is returned. I think that's
cleanest.

But yes, its a buyer-beware situation. So - "Smart Buyers Only"!

···

--
Posted via http://www.ruby-forum.com/\.

<snip>

>>> (1..10).to_a.to_ss
> => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
>>> (1..10).to_a.days
> => [86400, 172800, 259200, 345600, 432000, 518400, 604800, 691200,
> 777600, 864000]
>>> [2,'two', :two].classes
> => [Fixnum, String, Symbol]
>>> [2,'two', :two].classes.names
> => ["Fixnum", "String", "Symbol"]
>>> [2,'two', :two].classes.names.lengths
> => [6, 6, 6]
>
> So much happy syntax in one place!

Actually the reason this is kind of cool is not the syntax but the
semantics. It's much more expressive than any number of &:->()...
things that have been proposed -- and looks better. (There may be
some connection there too.)

I wonder, though, whether it would be too easy for it to trample on
other method names. I guess that's true of anything -- you'd just
have to make sure you didn't have two conceptions of some plural
thing.

I agree, *cool* and *somehow dangerous*, I would very conservatively propose
a proxy method, similiar in spirit to BDD's rspec#is

%w{the meaning of 42}.map.length => [3, too_long_to_count, 2, 2]

or maybe not overload #map without params for that purpose?

Just an idea of course :wink:

BTW Using English semantics for Ruby semantics does seem
+ interesting
+ avantguarde
- intolerant
- too dangerous
- too early

it is great people doing this, I would hate having it in the core though.
Robert

···

--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.

- Albert Einstein

Arrrrghh
I was quoting David; who was quoting Nic, sorry for my mistake
Robert

Hi --

<snip>

>>> (1..10).to_a.to_ss
> => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
>>> (1..10).to_a.days
> => [86400, 172800, 259200, 345600, 432000, 518400, 604800, 691200,
> 777600, 864000]
>>> [2,'two', :two].classes
> => [Fixnum, String, Symbol]
>>> [2,'two', :two].classes.names
> => ["Fixnum", "String", "Symbol"]
>>> [2,'two', :two].classes.names.lengths
> => [6, 6, 6]
>
> So much happy syntax in one place!

Actually the reason this is kind of cool is not the syntax but the
semantics. It's much more expressive than any number of &:->()...
things that have been proposed -- and looks better. (There may be
some connection there too.)

I wonder, though, whether it would be too easy for it to trample on
other method names. I guess that's true of anything -- you'd just
have to make sure you didn't have two conceptions of some plural
thing.

I agree, *cool* and *somehow dangerous*, I would very conservatively propose
a proxy method, similiar in spirit to BDD's rspec#is

%w{the meaning of 42}.map.length => [3, too_long_to_count, 2, 2]

or maybe not overload #map without params for that purpose?

I personally dislike "magic dot" stuff like that. True, it's not
really magic, since map can return some kind of enumerator that knows
about the array and is ready to do something to it. But it still has
the look and feel of a kind of secret system for communicating what's
wanted, rather than something that really makes sense when read left
to right.

That's why I prefer Dr. Nic's plurals thing, though it definitely has
the disadvantage of entering into murky territority in terms of method
naming.

David

···

On Thu, 28 Sep 2006, Robert Dober wrote:

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Robert Dober wrote:

I agree, *cool* and *somehow dangerous*, I would very conservatively
propose
a proxy method, similiar in spirit to BDD's rspec#is

%w{the meaning of 42}.map.length => [3, too_long_to_count, 2, 2]

or maybe not overload #map without params for that purpose?

I like this idea too - something that allows you to pick which iterator
method to use (that is: select, each, map)

Implementable syntax might be:

%w{the meaning of 42}.map_length

Where the Array.method_missing could parse it as:

          re = /(map|collect|select|each)_([\\w\\_]+)/
          if (match = method.to_s.match(re))
            puts match.inspect
            iterator, callmethod = match[1..2]
            puts [iterator, callmethod]
            return self.send(iterator) {|item| item.send callmethod}
          end

Cute. I've added it to the library.

···

--
Posted via http://www.ruby-forum.com/\.

Nice trick!

···

--

class Symbol
     def to_proc
         Proc.new { |obj, *args| obj.send(self, *args) }
     end
end

puts ["foo", "bar", "cat", "dog"].collect &:reverse!
oof
rab
tac
god

##

But why does this not work?

a = &:foo

sym.rb:9: parse error, unexpected tAMPER
a = &:reverse!

( Using ruby ruby 1.8.4 (2005-12-24) [i486-linux] )

--
Brad Phelan
Object Oriented Template Engine For Ruby ( alpha )
http://rubyforge.org/projects/galena/

Hi --

Actually the reason this is kind of cool is not the syntax but the
semantics. It's much more expressive than any number of &:->()...
things that have been proposed -- and looks better. (There may be
some connection there too.)

Agreed - its very nice semantically too. It has much more meaning than
.map(&:method_name) I think.

I wonder, though, whether it would be too easy for it to trample on
other method names. I guess that's true of anything -- you'd just
have to make sure you didn't have two conceptions of some plural
thing.

The method_missing code will perform "super" first to ensure that any
per-existing dynamic methods on the Array are evaluated first. Then it
attempts to call the method on the collection items. If they return a
NoMethodError then the original error object is returned. I think that's
cleanest.

I'm not sure that's working the way you think it is. When you call
super in method_missing, it's calling Object#method_missing, which
really just lives to be overridden. (Or is there some other layer
added in there somewhere?) Also, if a dynamic method of the same name
has been defined on the array, then method_missing won't get called in
the first place :slight_smile:

David

···

On Thu, 28 Sep 2006, Dr Nic wrote:

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Ahh. Removed trace and fixed the regular expression:

          re = /(map|collect|select|each|reject)_([\\w\\_]+\\??)/
          if (match = method.to_s.match(re))
            iterator, callmethod = match[1..2]
            return self.send(iterator) {|item| item.send callmethod}
          end

Now supports reject

For example:

list = [nil, 1, 2, nil, 4]

=> [nil, 1, 2, nil, 4]

list.reject_nil?

=> [1, 2, 4]

Cool.

···

--
Posted via http://www.ruby-forum.com/\.

Hi --

> <snip>
>>
>> >>> (1..10).to_a.to_ss
>> > => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
>> >>> (1..10).to_a.days
>> > => [86400, 172800, 259200, 345600, 432000, 518400, 604800, 691200,
>> > 777600, 864000]
>> >>> [2,'two', :two].classes
>> > => [Fixnum, String, Symbol]
>> >>> [2,'two', :two].classes.names
>> > => ["Fixnum", "String", "Symbol"]
>> >>> [2,'two', :two].classes.names.lengths
>> > => [6, 6, 6]
>> >
>> > So much happy syntax in one place!
>>
>> Actually the reason this is kind of cool is not the syntax but the
>> semantics. It's much more expressive than any number of &:->()...
>> things that have been proposed -- and looks better. (There may be
>> some connection there too.)
>>
>> I wonder, though, whether it would be too easy for it to trample on
>> other method names. I guess that's true of anything -- you'd just
>> have to make sure you didn't have two conceptions of some plural
>> thing.
>
> I agree, *cool* and *somehow dangerous*, I would very conservatively propose
> a proxy method, similiar in spirit to BDD's rspec#is
>
> %w{the meaning of 42}.map.length => [3, too_long_to_count, 2, 2]
>
> or maybe not overload #map without params for that purpose?

I personally dislike "magic dot" stuff like that. True, it's not
really magic, since map can return some kind of enumerator that knows
about the array and is ready to do something to it. But it still has
the look and feel of a kind of secret system for communicating what's
wanted, rather than something that really makes sense when read left
to right.

That's why I prefer Dr. Nic's plurals thing, though it definitely has
the disadvantage of entering into murky territority in terms of method
naming.

I like the idea of being able to treat messages like some kind of
object and manipulating stuff with them somewhat like this, so just
yesterday I made a mutated version of Symbol#to_proc, the "first
class" message:

class Message
  def initialize(message, *args)
    @message, @args = message, args
  end

  def call(x, *args)
    x.send @message, *(@args + args)
  end

  def to_proc
    lambda { |*args| call(*args) }
  end
end

if $0 == __FILE__
  # Example
  puts [*0..100].select(&Message.new(:>, 50)).inspect
  puts [*0..100].inject(&Message.new(:+))
  puts %w(Once upon a time).map(&Message.new(:upcase)).inspect
  puts Message.new(:index, /[aeiou]/, -3).call("hello")
  puts %w(1 2 3 4 5).map(&Message.new(:to_i)).map(&Message.new(:class)).inspect
end

$ ruby message.rb
[51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
5050
["ONCE", "UPON", "A", "TIME"]
4
[Fixnum, Fixnum, Fixnum, Fixnum, Fixnum]

The syntax isn't as terse, but it does have the advantage of not
interfering with existing methods (and you could just write a kernel
method as a shortcut if you like).

···

On 9/28/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

On Thu, 28 Sep 2006, Robert Dober wrote:

David

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

--
- Simen

unknown wrote:

I'm not sure that's working the way you think it is. When you call
super in method_missing, it's calling Object#method_missing, which
really just lives to be overridden. (Or is there some other layer
added in there somewhere?) Also, if a dynamic method of the same name
has been defined on the array, then method_missing won't get called in
the first place :slight_smile:
David

I've got it all bundled in a module, so it could be applied to another
class that did have a meaningful method_missing, but for Array your
point is valid.

By "dynamic method" I mean methods like find_by_<attr_name> in
ActiveRecord - methods that are accepted by the method_missing but don't
exist, rather than methods that are dynamically added to the class at
some point. Sorry if that's the wrong name for them.

Having said all that, I don't think this is working for associations on
ActiveRecords (which are just Arrays). I'm still investigating, but
perhaps rails adds its own method_missing that is negating this one?

Nic

···

--
Posted via http://www.ruby-forum.com/\.

dblack@wobblini.net wrote:

I personally dislike "magic dot" stuff like that. True, it's not
really magic, since map can return some kind of enumerator that knows
about the array and is ready to do something to it. But it still has
the look and feel of a kind of secret system for communicating what's
wanted, rather than something that really makes sense when read left
to right.

Agreed. That is similar to the reason I hate the flip-flop
operator (syntax, not semantics) and even worse, junctions.

Hal

FWIW I really prefer the magic underscore over anything else.

The pluralization thing has too much potential for collision and looks
jarring (to_as looks to me like "to" "as", which doesn't make any
sense; then again I'm not a fan of programmatic pluralization for any
reason, since it's very western-language-centric and far from
foolproof (moose? virus? fish?)).

The magic dot has a potential to create scads more objects to handle
the adapting. With a magic dot, list.map.to_i necessarily has to
create some adapter object for the call to map so there's a receiver
for to_i where no object was required before. Unless Ruby is adding a
pretty powerful GC in the future, perhaps we should avoid adding tons
of transient objects just for the magic dot.

The magic underscore has far less potential to cause a collision and
requires no intermediate objects to be created. And as others have
mentioned, it's been proven to look and feel really nice by
ActiveRecord.

···

On 9/28/06, Dr Nic <drnicwilliams@gmail.com> wrote:

I like this idea too - something that allows you to pick which iterator
method to use (that is: select, each, map)

Implementable syntax might be:

%w{the meaning of 42}.map_length

--
Contribute to RubySpec! @ Welcome to headius.com
Charles Oliver Nutter @ headius.blogspot.com
Ruby User @ ruby.mn

Simen Edvardsen wrote:

The syntax isn't as terse, but it does have the advantage of not
interfering with existing methods (and you could just write a kernel
method as a shortcut if you like).

Which would look like:

if $0 == __FILE__
   # Example
   puts [*0..100].select(&message(:>, 50)).inspect
   puts [*0..100].inject(&message(:+))
   puts %w(Once upon a time).map(&message(:upcase)).inspect
   puts message(:index, /[aeiou]/, -3).call("hello")
   puts %w(1 2 3 4
5).map(&message(:to_i)).map(&message(:class)).inspect
end

Not too shabby. But still I think terseness is the major advantage of
the Symbol#to_proc or the Magic Dot notation (like that name btw). OTOH
this might have other advantages.

T.

Hal Fulton wrote:

>
> I personally dislike "magic dot" stuff like that. True, it's not
> really magic, since map can return some kind of enumerator that knows
> about the array and is ready to do something to it. But it still has
> the look and feel of a kind of secret system for communicating what's
> wanted, rather than something that really makes sense when read left
> to right.
>

Agreed. That is similar to the reason I hate the flip-flop
operator (syntax, not semantics) and even worse, junctions.

I understand the general feeling you express. But is the reason really
just in the reading? I suspect there's more to it. What really lies at
the core of this uncomfortable feeling? I think maybe is has more to do
with comprehension of the general pattern. That is to ask, what is this
"magic dot" notation really? Maybe if we understood it better we would
not feel so uncomfortable with it --or at the very least, maybe we
would truly understand why we rightly feel so.

In thinking about it, it seems to me that "magic-dotting" is simply
using a form of Object Adapter Design Pattern. (see
http://en.wikipedia.org/wiki/Adapter_pattern\). I think there is a
tendency to over look this and think more in terms of retroactive
application of the subsequent method call. That means were focusing on
the ultimate action, and so it seems odd then b/c we are thinking of
'.x.y' in 'obj.x.y' as a single message when clearly we see there are
two messages actually being sent. That unsettles us. But as I suggest,
if we see it for what it really is --an adapter, then it makes perfect
sense, and doesn;t seem so magical after all (except that it still
offers some nicely terse syntax).

T.

···

dblack@wobblini.net wrote:

I like your summary.

The pluralization thing has too much potential for collision and looks
jarring (to_as looks to me like "to" "as", which doesn't make any
sense; then again I'm not a fan of programmatic pluralization for any
reason, since it's very western-language-centric and far from
foolproof (moose? virus? fish?)).

I agree that you shouldn't use it if the resulting code makes no sense
or might cause conflicts. If I've got an array of ActiveRecords that
have a name field, then calling @list.names to return an array of names
still seems to be readable and conflictless imo.

The magic dot has a potential to create scads more objects to handle
the adapting. With a magic dot, list.map.to_i necessarily has to
create some adapter object for the call to map so there's a receiver
for to_i where no object was required before. Unless Ruby is adding a
pretty powerful GC in the future, perhaps we should avoid adding tons
of transient objects just for the magic dot.

Perhaps the proxy could be associated with the class and reused?

The magic underscore has far less potential to cause a collision and
requires no intermediate objects to be created. And as others have
mentioned, it's been proven to look and feel really nice by
ActiveRecord.

I like this a lot too.

#1 and #3 are currently available in the .rb file on my blog page for
anyone finding this thread.

Nic

···

--
Posted via http://www.ruby-forum.com/\.

Hi --

I like this idea too - something that allows you to pick which iterator
method to use (that is: select, each, map)

Implementable syntax might be:

%w{the meaning of 42}.map_length

FWIW I really prefer the magic underscore over anything else.

I do too, generally, though map_length sounds like "the length of the
map of this object". One starts to think there's a reason that map
takes a block....

The pluralization thing has too much potential for collision and looks
jarring (to_as looks to me like "to" "as", which doesn't make any
sense; then again I'm not a fan of programmatic pluralization for any
reason, since it's very western-language-centric and far from
foolproof (moose? virus? fish?)).

Yeah, that was my point with sheep, even though I do rather like the
way the plural things read (when they work). How about:

   array.meese.each do |moose| ... end

The mind boggles :slight_smile:

David

···

On Fri, 29 Sep 2006, Charles O Nutter wrote:

On 9/28/06, Dr Nic <drnicwilliams@gmail.com> wrote:

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Charles O Nutter wrote:

> I like this idea too - something that allows you to pick which iterator
> method to use (that is: select, each, map)
>
> Implementable syntax might be:
>
> %w{the meaning of 42}.map_length

FWIW I really prefer the magic underscore over anything else.

The pluralization thing has too much potential for collision and looks
jarring (to_as looks to me like "to" "as", which doesn't make any
sense; then again I'm not a fan of programmatic pluralization for any
reason, since it's very western-language-centric and far from
foolproof (moose? virus? fish?)).

You are right there. Pluralization is really going overboard, besides
the exceptions it creates a great deal of computational overhead. It
seems great on the surface, but in the end it is simply is not worth
the effort. Although it can seem odd for the English speaker at times,
Ruby's general favoring of the singular is a very good thing. And this
is one area in which I feel Rails has been unhelpful.

The magic dot has a potential to create scads more objects to handle
the adapting. With a magic dot, list.map.to_i necessarily has to
create some adapter object for the call to map so there's a receiver
for to_i where no object was required before. Unless Ruby is adding a
pretty powerful GC in the future, perhaps we should avoid adding tons
of transient objects just for the magic dot.

Actually this is largely mitigated. Best practice is to cache the
adapter object. So for example Enumerable#every:

  def every
    @_functor_every ||= Functor.new do |op,*args|
      self.collect{ |a| a.send(op,*args) }
    end
  end

This is something not often shown in examples and admittedly may not
make it into first editions of such functions in practice, but
utlimately it gets incorporated and makes a huge difference in the over
head you mention.

The magic underscore has far less potential to cause a collision and
requires no intermediate objects to be created. And as others have
mentioned, it's been proven to look and feel really nice by
ActiveRecord.

I disagree. "Magic underscore"[1] is either a function of
method_missing or dynamic method creation. There is acually more
potential for name collision in these cases. IN fact that is one of the
uses of the magic dot, to create namespaces. Also care must be taken to
propogte method_missing through the class heirarchy so as not to step
on other people's magic toes. And dynamic method creation adds a lot of
additional methods to a class that Ruby must sort through when
dispatching a call. It certainly looks nice, and I'm in no way agasint
it's use. But there are tradoffs to be considered, and one should apply
that techinique that works best to the need at hand.

[1]FYI, "magic underscore" isn't an a good name b/c we're really just
talking about dynamic method names. These can be anything and do not
neccessarly need an underscore.

T.

···

On 9/28/06, Dr Nic <drnicwilliams@gmail.com> wrote: