Artem Syzonenko
Artem Syzonenko

Reputation: 43

Why Symbol#to_proc has such type of behaviour?

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?

Upvotes: 3

Views: 91

Answers (2)

andHapp
andHapp

Reputation: 3197

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.

Upvotes: 3

tadman
tadman

Reputation: 211610

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

Upvotes: 6

Related Questions