Skip to content
AgileLogics
6 min read

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, &block captures the block passed to a method as a proc object.
  • In a method call, &something turns 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 return inside a proc stored for later. If the method that created it has already finished, it raises a LocalJumpError.