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