1

Why does: respond_to? in:

class Wolf
  def howl; end
end

Wolf.new.respond_to?(:howl) # => true

not require & while map in:

["1", "2", "3"].map(&:to_i) # => [1, 2, 3]

does? Also, are there any technical names for this?

sawa
  • 165,429
  • 45
  • 277
  • 381
thedanotto
  • 6,895
  • 5
  • 45
  • 43

1 Answers1

2

When you say :method, you're using some nice syntactical sugar in ruby that creates a new Symbol object. When you throw an ampersand before it (&:method), you're using another piece of sugar. This invokes the to_proc method on the symbol.

So, these two things are identical:

method_proc = &:method

sym = :method
method_proc = method.to_proc

What's the difference between that and the other usage? Well, respond_to? has a single argument -- a symbol. So we can pass :method and be all fine and dandy. (Interestingly, objects do respond to the method named method, but that's a far more confusing question).

By comparison, Enumerable's iterators (like map, select, etc) accept a block. When we pass a Proc, it is interpreted properly as that block. So, these two pieces of code are equivalent:

[1,2,3].map { |i| i.even? }
[1,2,3].map(&:even?)

This equivalence is a little confusing, because of course Symbol has no idea that there's an even? method somewhere. To play around with it, I used evenproc = :even?.to_proc to inspect the resulting proc. It's implemented in C (at least in MRI ruby), and isn't willing to give up its source. However, its arity is -1, which means that it accepts one optional arg. My best guess is that it does something like this:

def to_proc
  method_name = self.to_s
  ->(a) { a.send(method_name) }
end

I could dig further, but I think we've already gone way past the question. ;) Good luck!

Joe Mastey
  • 26,809
  • 13
  • 80
  • 104