Procs and Lambdas in Ruby

· 5 min read · Updated March 7, 2026 · intermediate
procs lambdas closures blocks

Procs and lambdas are both ways to create callable blocks of code in Ruby. They’re powerful tools that let you pass behavior around like data. If you’ve used blocks in Ruby, procs and lambdas will feel familiar—but with extra capabilities.

In this tutorial, you’ll learn what procs and lambdas are, how they differ, and when to use each one.

What Is a Proc?

A proc is an instance of the Proc class that represents a block of code. You can store it in a variable, pass it around, and call it later.

Creating a Proc

There are two ways to create a proc:

# Using Proc.new
my_proc = Proc.new { |name| puts "Hello, #{name}!" }

# Using proc (Ruby 1.8.7+)
my_proc = proc { |name| puts "Hello, #{name}!" }

Calling a Proc

my_proc.call("Alice")
# => Hello, Alice!

# Alternative calling syntax
my_proc.()
my_proc===

What Is a Lambda?

A lambda is also a Proc object, but with some different characteristics. It behaves more like a method.

Creating a Lambda

# Using the lambda keyword
my_lambda = lambda { |name| puts "Hello, #{name}!" }

# Using the stabby lambda syntax (Ruby 1.9+)
my_lambda = ->(name) { puts "Hello, #{name}!" }

Calling a Lambda

my_lambda.call("Bob")
# => Hello, Bob!

# Alternative calling syntax
my_lambda.()

Key Differences

Understanding the differences between procs and lambdas is essential for writing idiomatic Ruby code.

1. Argument Handling (Arity)

Lambdas are strict about arguments, while procs are lenient:

# Proc: extra arguments are ignored, missing arguments become nil
my_proc = proc { |a, b| puts "a: #{a}, b: #{b}" }
my_proc.call(1)
# => a: 1, b: 

my_proc.call(1, 2, 3)
# => a: 1, b: 2

# Lambda: raises ArgumentError for wrong number of arguments
my_lambda = ->(a, b) { puts "a: #{a}, b: #{b}" }
my_lambda.call(1)
# => ArgumentError (wrong number of arguments (given 1, expected 2))

my_lambda.call(1, 2, 3)
# => ArgumentError (wrong number of arguments (given 3, expected 2))

2. Return Behavior

This is the most important difference:

# Proc: returns from the enclosing method
def method_with_proc
  my_proc = proc { return "returned from proc" }
  result = my_proc.call
  puts "This line never runs"
  result
end

method_with_proc
# => "returned from proc"

# Lambda: returns from itself, not the enclosing method
def method_with_lambda
  my_lambda = -> { return "returned from lambda" }
  result = my_lambda.call
  puts "This line runs!"
  result
end

method_with_lambda
# => This line runs!
# => "returned from lambda"

When to Use Each

Use Procs When:

  • You need flexible argument handling
  • You want the return behavior to exit the enclosing method
  • You’re building DSLs or configuration blocks
# Example: Configuration block
class Service
  def configure(&block)
    config = Config.new
    block.call(config) if block_given?
    config
  end
end

Use Lambdas When:

  • You want strict argument checking
  • You need behavior that behaves like a method
  • You’re passing callbacks that shouldn’t affect the calling code
# Example: Data transformation pipeline
transformations = [
  ->(x) { x.strip },
  ->(x) { x.upcase },
  ->(x) { x.reverse }
]

text = "  Hello World  "
transformations.each { |t| text = t.call(text) }
puts text
# => DLRO OLLEH

Practical Examples

Example 1: Custom Array Operations

# Using a proc for flexible filtering
filter_proc = proc { |item| item.is_a?(String) && item.length > 3 }
items = [1, "hi", "hello", 42, "world"]
filtered = items.select(&filter_proc)
# => ["hello", "world"]

# Using a lambda for transformation
upcase_lambda = ->(s) { s.upcase }
upcased = items.select(&:is_a?).map(&upcase_lambda)
# => ["HI", "HELLO", "WORLD"]

Example 2: Building a Simple Strategy Pattern

class PaymentProcessor
  def initialize(strategy)
    @strategy = strategy
  end

  def pay(amount)
    @strategy.call(amount)
  end
end

credit_card_strategy = ->(amount) { "Charging #{amount} to credit card" }
paypal_strategy = lambda { |amount| "Paying #{amount} via PayPal" }

processor = PaymentProcessor.new(credit_card_strategy)
puts processor.pay(100)
# => Charging 100 to credit card

processor = PaymentProcessor.new(paypal_strategy)
puts processor.pay(50)
# => Paying 50 via PayPal

Example 3: Delayed Execution

def create_delayed_task(delay, &task)
  Thread.new do
    sleep(delay)
    task.call
  end
end

task = create_delayed_task(1) { puts "Task executed after 1 second!" }
puts "Task scheduled"
task.join
# => Task scheduled
# => Task executed after 1 second!

Converting Between Blocks, Procs, and Lambdas

# Block to proc
def my_method(&block)
  block # This is now a Proc
end

# Proc to block
my_proc = proc { |x| puts x }
# Use & to convert back to block
[1, 2, 3].each(&my_proc)

# Lambda to proc
my_lambda = ->(x) { puts x }
my_proc = my_lambda.to_proc

# Proc to lambda (risky - changes behavior)
my_proc = proc { |x, y| puts x + y }
my_lambda = my_proc.lambda? ? my_proc : my_proc.clone

Common Pitfalls

Pitfall 1: Forgetting the Return Difference

# Wrong: Using lambda return in a loop context
def process_items(items)
  items.each do |item|
    next lambda { return } # This doesn't work as expected!
  end
end

# Right: Use proc for early returns
def process_items(items)
  items.each do |item|
    next proc { return }.call
  end
end

Pitfall 2: Confusing Symbol#to_proc with Lambdas

# This creates a proc from a symbol, not a lambda
:upcase.to_proc
# => #<Proc:0x00007f9a8c2d4e20>

# Use lambda for explicit behavior
upcase_lambda = ->(s) { s.upcase }
["hello", "world"].map(&upcase_lambda)
# => ["HELLO", "WORLD"]

Summary

FeatureProcLambda
Creationproc {} or Proc.new {}lambda {} or ->() {}
ArgumentsFlexible (ignores extra, nil for missing)Strict (raises on mismatch)
ReturnReturns from enclosing methodReturns from itself
Use caseDSLs, flexible callbacksData transformation, strategy pattern

Procs and lambdas are essential tools in Ruby. Master them, and you’ll write more flexible and expressive code.

In the next tutorial, we’ll explore the Enumerable module—your gateway to powerful collection manipulation in Ruby.