Question
In Ruby, especially around Ruby 1.8 and 1.9, there are subtle differences between proc, lambda, and Proc.new.
What are the differences between them?
When should you choose lambda instead of Proc.new, or vice versa?
Also, in Ruby 1.9 and later, proc and lambda behave differently. How should that be understood in practice?
Short Answer
By the end of this page, you will understand that Ruby lambda and Proc.new both create callable objects, but they behave differently in important ways. You will learn how they handle return, how strictly they check arguments, how proc changed across Ruby versions, and practical rules for choosing the right one in real code.
Concept
In Ruby, a Proc is an object that wraps a block of code so it can be stored, passed around, and called later.
That sounds simple, but Ruby has two block-like behaviors under the Proc umbrella:
- lambda-style behavior
- non-lambda proc behavior created with
Proc.new
Both are callable, but they differ in two major ways:
-
Argument checking
- A
lambdabehaves more like a regular method. - It checks the number of arguments more strictly.
- A non-lambda
Proc.newis more forgiving and may assignnilto missing parameters or ignore extra ones.
- A
-
returnbehavior- In a
lambda,returnreturns from the lambda itself. - In a non-lambda proc,
returntries to return from the surrounding method where the proc was created.
- In a
These differences matter because they affect program flow and error handling. A wrong choice can cause confusing bugs, especially when callbacks, iterators, or deferred logic are involved.
Mental Model
Think of both lambda and Proc.new as portable chunks of code.
But they have different personalities:
-
A lambda is like a mini method in a box.
- It expects the right inputs.
- It returns to its own caller.
- It behaves in a controlled, method-like way.
-
A non-lambda proc is like a loose block of code captured from its surroundings.
- It is more relaxed about inputs.
- It is more tied to the context where it was created.
- Its
returncan affect the outer method.
If you ask, "Which one feels more predictable and self-contained?" the answer is usually lambda.
If you ask, "Which one behaves more like a normal Ruby block passed around as an object?" the answer is usually Proc.new.
Syntax and Examples
Core syntax
my_lambda = lambda { |x| x * 2 }
my_lambda = ->(x) { x * 2 }
my_proc = Proc.new { |x| x * 2 }
my_proc = proc { |x| x * 2 }
All of these create callable objects. You can run them with .call:
my_lambda.call(5) # => 10
my_proc.call(5) # => 10
Argument behavior
Lambda checks arguments more strictly
double = ->(x) { x * 2 }
double.call(5) # => 10
double.call # ArgumentError
double.call(5, 6) # ArgumentError
Proc.new is more lenient
double = Proc.new { |x| x * 2 }
double.call(5) # => 10
double.call
Step by Step Execution
Consider this example:
def example
operation = Proc.new { return "stopped early" }
puts "Before call"
value = operation.call
puts "After call"
value
end
puts example
Step by step
- The method
examplestarts running. - A non-lambda proc is created and stored in
operation. - Ruby prints:
Before call
operation.callruns the proc body.- Inside the proc, Ruby sees:
return "stopped early"
- Because this is a non-lambda proc,
returntries to return from the surrounding method, which isexample. - The method exits immediately with the value
"stopped early". - The line
puts "After call"never runs.
Real World Use Cases
When lambdas are useful
Validations and transformations
If you want a callable rule that behaves predictably like a method, use a lambda.
normalize_name = ->(name) { name.strip.downcase }
normalize_name.call(" Alice ") # => "alice"
Callbacks with clear input requirements
formatter = ->(user) { "User: #{user[:name]}" }
This is useful when the callback must receive exactly the expected arguments.
Reusable business rules
adult_check = ->(age) { age >= 18 }
This is safe because the callable has clear expectations.
When Proc.new is useful
Block-like behavior
If you want something that behaves more like a normal Ruby block, Proc.new can make sense.
printer = Proc.new { |value| puts value }
Flexible callbacks
Sometimes you do not care if callers pass too many arguments.
log = .new { || puts message }
log.call(, .now)
Real Codebase Usage
In real Ruby projects, developers usually prefer lambdas when the callable represents a unit of logic with a defined contract.
Common lambda patterns
Configuration rules
PRICE_FORMATTER = ->(price) { format("$%.2f", price) }
Filtering and mapping helpers
active_only = ->(user) { user[:active] }
users.select(&active_only)
Guard-like logic
valid_email = ->(email) { email.include?("@") }
return unless valid_email.call(params[:email])
Because lambdas check arguments and contain their own return, they are safer in shared code.
Common Proc.new patterns
Capturing a block to store or reuse
def repeat_action(&block)
action = block
3.times { action.call }
end
The captured block is a Proc object.
Flexible internal DSLs
Common Mistakes
1. Assuming proc and lambda are the same
They are both Proc objects, but they do not behave the same.
p1 = proc { |x| x }
p2 = ->(x) { x }
p1.lambda? # => false
p2.lambda? # => true
2. Using return inside Proc.new without understanding the effect
Broken or surprising example:
def wrapper
fn = Proc.new { return "done" }
fn.call
"still running"
end
wrapper # => "done"
If you expected "still running", use a lambda instead.
def wrapper
fn = -> { return "done" }
fn.call
"still running"
end
wrapper
Comparisons
lambda vs Proc.new
| Feature | lambda | Proc.new / proc |
|---|---|---|
| Object type | Proc | Proc |
lambda? | true | false |
| Argument checking | Strict, like a method | Lenient |
return | Returns from itself | Returns from surrounding method |
| Best for | Method-like callable logic | Block-like behavior |
Cheat Sheet
# Lambda
l = ->(x) { x * 2 }
l.lambda? # => true
l.call(3) # => 6
# Non-lambda proc
p = Proc.new { |x| x * 2 }
p.lambda? # => false
p.call(3) # => 6
Key rules
lambdais method-like.Proc.newis block-like.procin Ruby 1.9+ behaves likeProc.new.
Arguments
->(a, b) { [a, b] }.call(1)
# ArgumentError
Proc.new { |a, b| [a, b] }.call(1)
# => [1, nil]
Return behavior
def a
fn = -> { return "x" }
fn.call
"after"
FAQ
What is the main difference between lambda and Proc.new in Ruby?
The main differences are argument checking and return behavior. Lambdas are strict and method-like, while Proc.new is looser and behaves more like a regular block.
Is proc the same as Proc.new in Ruby?
In Ruby 1.9 and later, yes in practice: proc { ... } creates a non-lambda proc, like Proc.new { ... }.
Should I use lambda or Proc.new for callbacks?
Use lambda when the callback should have strict inputs and predictable control flow. Use Proc.new when you want true block-like flexibility.
Why does return inside a proc sometimes exit the outer method?
Because a non-lambda proc keeps block-like return semantics. Its return targets the surrounding method where it was defined.
How do I know whether a Proc is a lambda?
Use:
Mini Project
Description
Build a small Ruby script that stores reusable pricing rules as callables. This project demonstrates when lambdas are safer than non-lambda procs by showing strict arguments and predictable return behavior in a realistic business example.
Goal
Create a script that applies a discount rule and a tax rule to a product price using lambdas, then compare one behavior with a non-lambda proc.
Requirements
- Create a lambda that applies a percentage discount to a price.
- Create a lambda that adds tax to a price.
- Call both lambdas in sequence to calculate a final price.
- Add one
Proc.newexample that shows lenient argument handling. - Print the intermediate and final results clearly.
Keep learning
Related questions
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.
Convert a Unix Timestamp to Ruby DateTime
Learn how to convert Unix timestamps to Ruby DateTime and Time objects, with examples, differences, pitfalls, and practical Ruby usage.