Procs and Lambdas
Prerequisites: Blocks
A block is attached to one method call and disappears afterwards. When you want to store a piece of code in a variable, pass it around, and call it later, you turn it into an object: a proc or a lambda.
Lambdas
square = ->(x) { x * x }
add = lambda { |a, b| a + b }
p square.call(4)
p square.(5)
p square[6]
p add.call(2, 3)
16
25
36
5
->(x) { ... } (the "stabby lambda") is the common way to write one.
.call, .(), and [] all run it.
Passing code around
Stored code is useful when the behavior should be a parameter:
DISCOUNTS = {
none: ->(price) { price },
half: ->(price) { price / 2.0 },
ten_off: ->(price) { [price - 10, 0].max },
}
def checkout(price, rule)
DISCOUNTS.fetch(rule).call(price)
end
p checkout(30, :half)
p checkout(30, :ten_off)
15.0
20
Blocks, procs, and &
& converts between blocks and proc objects:
- In a parameter list,
&blockcaptures the block passed to a method as a proc object. - In a method call,
&somethingturns a proc (or lambda) back into a block.
def run_twice(&block)
block.call
block.call
end
run_twice { puts "hi" }
shout = ->(word) { word.upcase }
p %w[a b].map(&shout)
hi
hi
["A", "B"]
map(&:upcase) works the same way: & converts the symbol :upcase
into a proc that calls that method.
Proc vs lambda
Proc.new { } and proc { } create plain procs. The two differ in
two ways that matter:
| Lambda | Proc | |
|---|---|---|
| Wrong number of arguments | Raises ArgumentError |
Ignores extras, fills missing with nil |
return inside |
Returns from the lambda | Returns from the enclosing method |
Lambdas behave like small methods, which makes them more predictable. Prefer lambdas when you store code yourself.
Closures
Procs and lambdas remember the variables around them when they were created — they're closures:
def counter
count = 0
-> { count += 1 }
end
tick = counter
tick.call
tick.call
p tick.call
3
count would normally disappear when counter returns, but the lambda
keeps it alive.
Common mistakes
- Calling a lambda with the wrong number of arguments —
wrong number of arguments (given 1, expected 2). - Using
returninside a proc stored for later. If the method that created it has already finished, it raises aLocalJumpError.