Ruby: Procs and Lambdas
Procs
Blocks cannot be assigned to variables to be reused dynamically because they are not objects. If you want to store a block for repeated use, you can do this using a Proc.
Proc objects are blocks that have been assigned to a variables. Once assigned, the code may be called in different contexts again and again using those variables. Create a Proc by instantiating it with the #new
method, and are then called using an ampersand (&
).
square_numbers = Proc.new do |n|
n ** 2
end
[1, 2, 3].collect!(&square_numbers)
#=> [1, 4, 9]
[4, 5, 6].map!(&square_numbers)
#=> [16, 25, 36]
Procs are very useful because they help to keep your code DRY (Don’t Repeat Yourself) and also have all the qualities of objects where blocks don’t. Procs can be yield
ed and call
ed, like so:
def banana
yield
end
peel = Proc.new do
puts "ready to eat!"
end
banana(&peel)
#=> "ready to eat!"
def banana
peel.call
end
peel = Proc.new do
puts "ready to eat!"
end
#=> "ready to eat!"
Symbols as Procs
The ampersand can be used to convert a Symbol into a Proc. Ruby method names can be Symbols. This means we can use the ampersand to pass around methods! That is a bit cool, right? Check out this example to see how this works:
numbers = [1, 2, 3, 4, 5]
strings = numbers.map!(&:to_s)
#=> ["1", "2", "3", "4", "5"]
Lambdas
A lambda belongs to the Proc class and so inherits all its methods, but has a few key differences. A lambda can be thought of as a method with no name, as it acts very much like a method. You see, where a Proc will run its code even if it holds an incorrect number of arguments (it just assigns nil
to unaccountable ones), a lambda will throw an error. It checks the number of arguments for its parameters just like a method. The other difference is that when a lambda is returned it passes control back to the calling method, whereas when a proc is returned it behaves like it is part of the calling method itself and exists inside it. This will present itself like this:
def show_lambda_result
lambda { return "we just returned from the block" }.call
return "we just returned from the calling method"
end
puts show_lambda_result
#=> "we just returned from the calling method"
def show_proc_result
Proc.new { return "we just returned from the block" }.call
return "we just returned from the calling method"
end
puts show_proc_result
#=> "we just returned from the block"
See how the lambda is called then passes control to the return within the method, whereas the Proc calls and returns so the following line is not returned.