Procs and Lambdas
Procs and lambdas are blocks of code you can store in variables, pass around, and call later. They're the foundation of functional programming in Ruby. Understanding when to use each one will make your code much more flexible.
double = Proc.new { |x| x * 2 }
puts double.call(5) # => 10
puts double.call(10) # => 20
triple = lambda { |x| x * 3 }
puts triple.call(5) # => 15
# Short lambda syntax
square = ->(x) { x ** 2 }
puts square.call(7) # => 49
Try it Yourself ->
lambda vs Proc
Lambdas are strict about arguments. If you pass the wrong number, you get an error. Procs are lenient โ missing arguments become nil, extra arguments are ignored. Lambdas also return from themselves, while Procs return from the enclosing method.
# Lambda: strict
greet = lambda { |name| "Hello, #{name}!" }
# greet.call() # => ArgumentError: wrong number of arguments
# Proc: lenient
greet = Proc.new { |name| "Hello, #{name}!" }
puts greet.call() # => "Hello, !" (name is nil)
puts greet.call("Bob") # => "Hello, Bob!"
# Return behavior
def test_proc
p = Proc.new { return "from proc" }
p.call
"from method" # This line never runs
end
def test_lambda
l = lambda { return "from lambda" }
l.call
"from method" # This line runs
end
puts test_proc # => "from proc"
puts test_lambda # => "from method"
Try it Yourself ->
Blocks and yield
Blocks are the most common way to pass code to methods in Ruby. Use yield inside a method to call the block. block_given? checks if a block was provided.
def repeat(times)
times.times do
yield
end
end
repeat(3) { puts "Hello!" }
# Hello!
# Hello!
# Hello!
def calculate(a, b)
if block_given?
yield(a, b)
else
a + b
end
end
puts calculate(3, 4) # => 7
puts calculate(3, 4) { |x, y| x * y } # => 12
Try it Yourself ->
The &block Parameter
You can capture a block as a Proc object using &block as a parameter. Then you can store it, pass it around, or call it later with call.
def collect(&block)
block.call("first")
block.call("second")
end
collect { |item| puts "Got: #{item}" }
# Got: first
# Got: second
def wrap(prefix, &block)
puts "#{prefix}: start"
result = block.call
puts "#{prefix}: end"
result
end
wrap("Task") { 42 * 2 }
# Task: start
# Task: end
Try it Yourself ->
Closures
Procs and lambdas are closures โ they capture the variables from the surrounding scope. Even after the scope ends, the closure remembers those variables.
def make_counter
count = 0
incrementer = -> { count += 1; count }
incrementer
end
counter = make_counter
puts counter.call # => 1
puts counter.call # => 2
puts counter.call # => 3
def make_multiplier(factor)
->(x) { x * factor }
end
double = make_multiplier(2)
triple = make_multiplier(3)
puts double.call(5) # => 10
puts triple.call(5) # => 15
Try it Yourself ->