Kernel#lambda
lambda { |params| block } -> proc lambda is a Kernel method that creates an anonymous function (a Proc object) that behaves like a closure. Unlike plain blocks, lambdas are objects that can be stored in variables, passed to methods, and returned from methods. They enforce strict argument checking compared to procs.
Syntax
lambda { |params| block }
lambda do |params|
# block body
end
->(params) { block } #stabby lambda syntax (Ruby 1.9+)
The curly-brace form works best for single-line lambdas, while the do...end form is clearer when the body spans multiple lines. The stabby arrow syntax -> is the most compact and has become the community preference for new Ruby code.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
block | block | required | The code to execute when the lambda is called |
Examples
Basic usage
square = lambda { |x| x ** 2 }
square.call(5)
# => 25
# Or using .()
square.(5)
# => 25
The .call method is the explicit way to invoke a lambda, but the .() shorthand reads more naturally in most contexts. Both forms are equivalent and can be used interchangeably depending on your style preference. The [] syntax also works for calling a lambda, though it is less common because it can be confused with array access at a glance.
Stabby lambda syntax
cube = ->(x) { x ** 3 }
cube.call(3)
# => 27
# Multiple parameters
add = ->(a, b) { a + b }
add.call(10, 5)
# => 15
The stabby lambda syntax reads like a mathematical function definition, which makes it a natural fit for short transformations. The arrow clearly separates the parameter list from the body, and multiple parameters are listed inside the parentheses just like a method definition.
Passing lambdas to methods
def apply_operation(num, operation)
operation.call(num)
end
double = ->(x) { x * 2 }
apply_operation(10, double)
# => 20
Passing a lambda as an argument treats it like any other object: you receive it as a parameter and call it with .call or .(). This pattern appears frequently in higher-order methods that accept a behavior as input, such as map, select, or custom pipeline steps.
Lambda vs proc: argument checking
The key difference between lambdas and procs is argument handling:
# Lambda: strict argument checking
square = lambda { |x| x ** 2 }
square.call(5, 10)
# => ArgumentError: wrong number of arguments (given 2, expected 1)
# Proc: loose argument checking
my_proc = proc { |x, y| [x, y] }
my_proc.call(5, 10)
# => [5, 10]
my_proc.call(5)
# => [5, nil]
A proc fills missing arguments with nil instead of raising an error, which can mask bugs if you are not careful. The strictness of lambdas makes them a safer default when you want argument mismatches to be caught early rather than silently producing incorrect results.
Return behavior
Lambdas return like methods, procs return like blocks:
def test_lambda
l = lambda { return 100 }
l.call
"after lambda" # This executes
end
def test_proc
p = proc { return 200 }
p.call
"after proc" # This does NOT execute - method returns 200
end
test_lambda # => "after lambda"
test_proc # => 200
A return inside a lambda exits only the lambda itself, while a return inside a proc exits the enclosing method. This difference in return semantics is the other major distinction between the two and is important to understand when refactoring blocks into named lambda objects.
Common patterns
Using lambdas as callback functions
class EventHandler
def initialize
@handlers = {}
end
def on(event, &callback)
@handlers[event] = callback
end
def trigger(event, *args)
@handlers[event]&.call(*args)
end
end
handler = EventHandler.new
handler.on(:click) { |data| puts "Clicked: #{data}" }
handler.trigger(:click, "button")
# Clicked: button
This event handler pattern stores lambdas as callbacks keyed by event name. When an event fires, the matching lambda receives the event data and runs. Because lambdas are objects, you can store them, replace them, and compose them without changing the handler class itself.
Currying
add = ->(a, b) { a + b }
add_five = add.curry[5]
add_five(10)
# => 15
Currying transforms a lambda that takes multiple arguments into a chain of lambdas each taking a single argument. Calling curry with no arguments returns a curried proc; passing a specific number of arguments creates a partially applied function that waits for the remaining arguments.
Composition
square = ->(x) { x ** 2 }
double = ->(x) { x * 2 }
composed = ->(x) { square.call(double.call(x)) }
composed.call(5)
# => 100 (5*2 = 10, 10**2 = 100)
Keeping lambda logic explicit
lambda works well when the code needs a reusable block of behavior with clear argument rules and a return value that acts like a method. That makes it a strong choice for callbacks, small pipelines, and helper objects that need to be passed around. The block should stay focused on one job so the caller can see what the lambda expects and what it returns. When the work gets broader, a named method may be easier to maintain than a heavily layered anonymous function.
Because lambdas are values, they can be stored, reused, and combined without forcing the program to duplicate the same block in several places. That makes them useful in code that builds a small toolkit of operations and then chooses one later. The main thing to watch is that the lambda remains readable on its own. If the block starts to hide too much state or too many branches, the caller loses the clarity that made the lambda attractive in the first place.
The strict argument handling also helps surface mistakes early, which is useful in callback-heavy code. If the caller passes the wrong number of values, the error appears right away instead of letting the block guess at missing input. That clear failure mode is part of what makes lambdas feel more method-like than ordinary blocks, and it is often exactly what a small reusable function needs.