Table of contentsClick link to navigate to the desired location
This content has been automatically translated from Ukrainian.
At first glance, such code seems strange - we usually write map(&:to_s) or map { |x| ... }. But Ruby allows you to pass not only characters, but any objects after &if they can turn into Proc through the to_proc method.
When Ruby sees &Person, it calls: Person.to_procThat is, trying to turn Person into a Proc object.
The resulting Proc object is used as block for the map method.
Next, Ruby calls this Proc - for each element of the array proc.call(element)
Example
class Person
def initialize(name)
@name = name
end
def self.to_proc
->(name) { new(name) }
end
end
people = ["Bant", "Yuki", "Beanie"].map(&Person)
p people
Result:
[#<Person:0x0000000103f2b3a8 @name="Bant">, #<Person:0x0000000103f2b0b0 @name="Yuki">, #<Person:0x0000000103f2adb8 @name="Beanie">]
What really happens under the hood?
Ruby calls yours Person.to_proc, which returns lambda:
->(name) { Person.new(name) } # note that we missed Person in the method (optional to specify))
And then actually does the same thing as:
["Oleh", "Ira", "Marta"].map { |name| Person.new(name) }
How does .call work?
Each Proc or lambda has a.call method that runs stored code inside. That is, when Ruby makes a proc.call(element), it actually executes your block:
my_proc = ->(x) { puts "Hello, #{x}" }
my_proc.call("Ruby") # => "Hello, Ruby"
In short:
- &obj in Ruby → calls obj.to_proc.
- The result must be Proc or lambda.
- Ruby then calls proc.call(x) for each element.
- Therefore map(&Person) works if the class has the self.to_proc method.
We will consider the last point in a little more detail. If the class does not have a .to_proc method - wait for an error. Why?
[1, 2, 3].map(&123)
What Ruby does:
- sees &123
- thinks: “yes, you need to call 123.to_proc”
- but in numbers (Integer) there is no method to_proc
- so - an error
irb(main):001> 123.to_proc (irb):1:in `<main>’: undefined method osulto_proc' for 123:Integer (NoMethodError) 123.to_proc ^^^^^^^^ Did you mean? to_r from /Users/mykyta/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/irb-1.15.2/exe/irb:9:in `<top (required)>' from/Users/mykyta/.rbenv/versions/3.2.1/bin/irb:25:in lessload' from /Users/mykyta/.rbenv/versions/3.2.1/bin/irb:25:in `<main>’ irb(main):002>
This post doesn't have any additions from the author yet.