Procs, lambdas and method objects
Proc and lambda side by side
A block is a piece of code a method runs; a Proc is that piece of code as an object a program can store, pass and call. A lambda is a Proc with a stricter idea of what a call looks like. For a matching call they do the same thing:
adder = proc { |a, b| a + b }
times = ->(a, b) { a + b }
p adder.call(1, 2)
p times.call(1, 2)
ruby -e 'p1 = proc { |a, b| a + b }; l = ->(a, b) { a + b }; p p1.call(1, 2), l.call(1, 2)'
3
3
proc { }, Proc.new { } and lambda { } are the three ways to build one; ->(args) { } is the short lambda. The difference appears when the call does not match the parameters.
Arity and return
A proc is loose: extra arguments are dropped and a missing one is nil. A lambda checks the count like a method and raises ArgumentError. The second difference is return: in a proc it returns from the method that is running, in a lambda it returns from the lambda only:
def from_proc
pr = proc { return 1 }
pr.call
2
end
def from_lambda
l = -> { return 1 }
l.call
2
end
p from_proc
p from_lambda
ruby -e 'def a; pr = proc { return 1 }; pr.call; 2; end; def b; l = -> { return 1 }; l.call; 2; end; p a; p b'
1
2
The proc left the method at the return, so the two was never reached; the lambda returned to the line after the call. That is why a callback stored for later is a lambda: a proc that returns would leave whichever method happens to call it.
&:symbol and method objects
&:upcase is the shortest block in Ruby. The ampersand converts the symbol to a proc that calls the named method on each item:
ruby -e 'p ["a", "b"].map(&:upcase)'
["A", "B"]
Only a method that takes no arguments fits the form. For anything with a parameter, use a block or a lambda: map { |s| s.center(5) }. A method can also be taken off an object as a value, which is what a callback list wants:
ruby -e 'm = 2.method(:+); p m.call(3)'
5
2.method(:+) is a Method object; call runs it, arity says how many arguments it wants, and to_proc turns it into the block form. The difference from a lambda is the receiver: a Method keeps the object it came from.
Currying
curry turns a lambda of two arguments into a chain of one-argument calls. It is how a general function becomes a specific one without a wrapper:
ruby -e 'add = ->(a, b) { a + b }.curry; p add.(1).(2)'
3
add.(1) returns a lambda waiting for the second argument; the next .(2) completes the call. add.call(1).call(2) is the same. Currying is rare in application code and common in code that builds other functions, such as a parser or a validation pipeline.
What to watch for
A proc and a lambda print differently, and the difference matters when a
call goes wrong: the inspect of p ->{} ends
with (lambda), the inspect of
p proc {} does not. When an
ArgumentError appears in a callback, check
whether the value was built with proc or with
->.
&:name on a missing method fails at the first
item with NoMethodError, which is the useful
failure: the name is wrong, not the data.
Next: Recursion in Ruby
Article author: Arthur Isaev