An answer to a question I posed yesterday on here was the following piece of Ruby code:
def overlap?(r1,r2)
r1.include?(r2.begin) || r2.include?(r1.begin)
When you prefix the last argument of a call with &
you are making clear that you are sending a block and not a normal argument. Ok, in method(&:something)
, :something
is a symbol, not a proc, so Ruby automatically calls the method to_proc
to get a real block. And Rails guys (and now also vanilla Ruby) cleverly defined it as:
class Symbol
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
end
That's why you can do:
>> [1, 2, 3].map(&:to_s) # instead of [1, 2, 3].map { |n| n.to_s }
=> ["1", "2", "3"]
[edit] Note: when you realize that this construction is no syntatic sugar but generic infrastructure that Ruby provides, nothing stops you from implementing your own to_proc
for other classes. Never felt limited because &:method
allowed no arguments?
class Array
def to_proc
proc { |obj, *args| obj.send(*(self + args)) }
end
end
>> ["1", "F", "FF"].map(&[:to_i, 16])
=> [1, 15, 255]
my_method(&some_value)
means to invoke my_method
, passing some_value
in the special argument slot, the proc-slot, usually reserved for passing do-notation blocks.
my_block = lambda { puts "hello" }
(1..3).each(&my_block)
Any object which is a Proc
or which responds to to_proc
is permitted to be passed in the proc-slot. If you pass in an object which is not a Proc
but which responds to to_proc
, then Ruby will call to_proc
on the object for you and pass the result into the method.
The implementation of Symbol#to_proc
is to return a proc which, when passed an argument, sends that argument the message that is the symbol itself. For example, :hello.to_proc.call(my_obj)
will end up doing my_obj.send :hello
.
So my_array.each(&:hello)
passes :hello
to each
in the proc-slot (where a block would normally passed, if you used the do-notation to make a block). :hello.to_proc.call(my_array[0])
ends up being my_array[0].send :hello
, and the same for all subsequent indexes of my_array
.
it equals:
ranges.sort_by{|r| r.begin}