3

I found this sample code that realizes custom Symbol#to_proc in Ruby:

class Symbol
  def to_proc
    puts "In the new Symbol#to_proc!"
    Proc.new { |obj| obj.send(self) }
  end
end

It includes additional "puts ..." string to ensure it is not built-in method. When I execute code

p %w{ david black }.map(&:capitalize)

the result is:

In the new Symbol#to_proc!
["David", "Black"]

But why it is not something like this?

In the new Symbol#to_proc!
["David"]
In the new Symbol#to_proc!
["Black"]

My logic is like this: map yields elements one by one to block. Block takes first element and executes .to_proc, than second. But why puts executes once only?

2 Answers2

6

The to_proc method is called once to return a Proc object that is then used repeatedly, so you're seeing the correct behavior.

If you moved the puts inside, you'd see what you're expecting:

class Symbol
  def to_proc
    Proc.new { |obj|
      puts "In the new Symbol#to_proc!"
      obj.send(self)
    }
  end
end
tadman
  • 208,517
  • 23
  • 234
  • 262
  • Thank you for explanation, but I can't get why Proc object creates one time only. Why not new Proc object for every map block call? I am sure that Ruby executes code correctly, but I can't find a good explanation for this situation. – Artem Syzonenko Jan 30 '14 at 16:51
  • 1
    Why would you want a brand new proc for each loop? That'd be extremely wasteful since the whole idea of `map` and similar functions is to apply exactly the same function to a number of objects. If you want to apply a different function to each element you should be using `each` with a custom bit of code in there. That gives you maximum flexibility. – tadman Jan 30 '14 at 16:53
3

In Ruby, map works with a block. The & operator calls to_proc on object following it and passes the value returned from calling to_proc to map as a block. With this information, let's look at your example again. In your example, &:capitalize will result in a call to to_proc method on :capitalize. Since, :capitalize is a Symbol, it will call to_proc on Symbol class, re-defined by you.

:capitalize.to_proc

will return:

In the new Symbol#to_proc!
=> #<Proc:0x007fa08183df28@(irb):4>

The & operator will use the returned Proc object, and pass that proc object to map as a block. In your re-defined to_proc method definition, puts is just getting executed and since puts prints to the console (assuming you are running this in console), you will see it printed. It's never passed to map, so you never see it printed twice.

However, if you would like the behaviour that you expect, use the first answer. Hope it helps.

andHapp
  • 3,149
  • 2
  • 21
  • 20