Search code examples
rubyproc

How does Ampersand pass arguments into #to_proc as "obj"? —receiving_method(&:method)


receiver_method(&:method) functionality seems clear enough, yet I find a gap in the explanations* about the assignment to 'obj' as in—

class Symbol
  def to_proc #<parameter notably absent>
    Proc.new { |obj, *args|
      obj.send (self, *args)
    }
  end
end

How does this assign the receiver method's object/passed argument as 'obj'?


* What does map(&:name) mean in Ruby?

* https://www.skorks.com/2013/04/ruby-ampersand-parameter-demystified/


Solution

  • How does Ampersand pass arguments into #to_proc as “obj”? — receiving_method(&:method)

    Ampersand does not pass anything anywhere. Ampersand converts the argument to a proc instance, implicitly calling to_proc on it. And passes the result as a block to the caller.

    Let’s stick with the example:

    %w[42 foo].map(&:to_i)
    #⇒ [42, 0]
    

    What’s going on here?

    to_i is being converted to proc as you shown in the OP

    #                            ⇓⇓⇓⇓⇓
    proc { |obj, *args| obj.send(:to_i, *args) }
    

    • we pass this proc to the caller (without lose of generality, I’d write it with block syntax for the sake of clarity

    %w[42 foo].map do |obj, *args|
      obj.send(:to_i, *args)
    end
    

    NB! *args is off-side here, since map passes the single argument to the block:

    %w[42 foo].map do |obj|
      obj.send(:to_i)
    end
    

    That would map:

    '42' → '42'.send(:to_i) ≡ '42'.to_i → 42,
    'foo' → 'foo'.send(:to_i) ≡ 'foo'.to_i → 0,
    

    yielding:

    #⇒ [42, 0]