Updated October 2026: Corrected the error messages and irb outputs for current Ruby, clarified what & does, renamed variables that shadowed Kernel#proc, and added the stabby lambda and the _1/it block parameters.

I. Anonymous Functions: The Proc Class

One of the significant Callable Objects in Ruby is Proc objects and Lambdas. Proc objects inherently contain sequences of code lines and can be initialized, stored, or passed with arguments, and when needed, executed using the call method.

  • Understanding Proc objects involves grasping the following:
    • Creating and using procs.
    • How procs utilize arguments and pass variables.
    • The concept of closure in procs.
    • Similarities and differences between procs and code blocks.
    • Differences between Proc and Lambda.

Let’s start by creating an instance of Proc object using the statement: Proc.new

proc_object = Proc.new { puts "Inside a Proc's block" }
 => #<Proc:0x00000002839f58 (irb):7>
proc_object.call
Inside a Proc's block
  • The proc method

proc is a method similar to Proc.new; both create Proc objects and return the same result.

Example:

proc_object_1 = proc { puts "Hi!" }
 => #<Proc:0x00000002839f58 (irb):7>
proc_object_1.call
Hi!
proc_object_1.class
 => Proc

proc_object_2 = Proc.new { puts "Hi!" }
 => #<Proc:0x00000002853408 (irb):10>
proc_object_2.call
Hi!
proc_object_2.class
 => Proc

II. How are Proc and Block different?

When creating a Proc object, we always need to provide a code block, but not every code block is a Proc or serves a specific Proc.

Example: [1, 2, 3].each { |x| puts x * 10 }

The above code block does not create a Proc object.

Digging deeper, a method can take a block and convert that block into a Proc object using a special syntax &.

We define a method with a block as an argument. In Ruby, it’s called Capture the block - USING PROCS FOR BLOCKS

Example:

def capture_block(&block)
 puts "convert block into proc and call"
 block.call
 puts "block class"
 block.class
end
=> :capture_block

capture_block { puts "I'm a Proc or Block" }
convert block into proc and call
I'm a Proc or Block
block class
=> Proc

It’s important to understand what & is used for. It works in both directions:

  1. In a method definition, def capture_block(&block), the & captures the block passed to the method and makes it available as a Proc object named block, which you can store or execute with call.
  2. At a call site, capture_block(&obj), the & turns obj back into a block. If obj is not already a Proc, Ruby calls obj.to_proc first. This is why %w[a b].map(&:upcase) works: Symbol#to_proc builds the proc.

So we cannot call capture_block like capture_block(p) or capture_block(p.to_proc) with p being a Proc: without &, Ruby treats it as a regular positional argument, and since capture_block accepts no positional arguments, it raises ArgumentError: wrong number of arguments (given 1, expected 0). Pass it as a block instead: capture_block(&p).

Additionally, a Proc can also be called within a code block using &.

Example:

printer = Proc.new { |x| puts x.upcase }
%w{ ruby and coffee }.each(&printer)
RUBY
AND
COFFEE
=> ["ruby", "and", "coffee"]

III. Lambda and the differences from proc

Similar to Proc.new, the lambda method also creates a Proc object.

lam = lambda { puts "A lambda!" }
=> #<Proc:0x441bdc (irb):13 (lambda)>
lam.call
A lambda!

The “stabby lambda” syntax -> is the more common way to write a lambda today:

double = ->(x) { x * 2 }
=> #<Proc:0x000000012a0f1c28 (irb):15 (lambda)>
double.call(3)
=> 6
double.(3)   # shorthand for call
=> 6
double[3]    # also calls it
=> 6

Lambda and proc differ in three points:

  • Lambda is explicitly initialized. Where Ruby implicitly initializes Proc objects, those created are regular procs, not lambdas. For example, in the case mentioned earlier with def capture_block(&block), the block in the capture_block method is implicitly converted into a Proc object; this block is not a lambda because a lambda must be explicitly initialized, not implicitly created. You can check with Proc#lambda?.

  • Lambda and proc differ in how they handle the return method. If a return statement is executed inside a lambda, it will return out of that lambda to the calling context. Whereas, if a return is inside a proc, it will return out of the method. The following example illustrates this: the output only prints “Still here!” because the proc returns out of the method.

def return_test
  l = lambda { return }
  l.call
  puts "Still here!"
  pr = Proc.new { return }
  pr.call
  puts "You won't see this message!"
end

return_test

Still here!
 => nil
  • Lastly, lambda won’t execute if passed the wrong number of arguments.
lam = lambda { |x| p x }
=> #<Proc:0x42ee9c (irb):21 (lambda)>

lam.call(1)
1
=> 1

lam.call
ArgumentError: wrong number of arguments (given 0, expected 1)

lam.call(1, 2, 3)
ArgumentError: wrong number of arguments (given 3, expected 1)

my_proc = proc { |x| p x }
=> #<Proc:0x42f1a8 (irb):27>

my_proc.call(1)
1
=> 1

my_proc.call
nil
=> nil

my_proc.call(1, 2, 3)
1
=> 1

In summary, lambda follows stricter logical reasoning. Meanwhile, proc is more lenient.

IV. Bonus: Implicit Block Parameters

For short blocks, you don’t have to name the parameter. Ruby 2.7 added numbered parameters (_1, _2, …), and Ruby 3.4 added it to reference the single block parameter (Ruby 3.4 NEWS):

[1, 2, 3].map { |x| x * 10 }  # => [10, 20, 30]
[1, 2, 3].map { _1 * 10 }     # => [10, 20, 30]
[1, 2, 3].map { it * 10 }     # => [10, 20, 30], Ruby 3.4+