Kernel#proc

proc { |params| block } -> Proc
Returns: Proc · Added in v1.8.7 · Updated March 13, 2026 · Kernel Methods
procs blocks closures

Kernel#proc creates a new Proc object by capturing the current block of code. Procs are closures — they retain access to variables from their defining scope even when called elsewhere. This makes them powerful for deferred execution, callbacks, and dynamic code organization.

Syntax

proc { |params| block }
Proc.new { |params| block }

The shorthand proc { } is equivalent to Proc.new { }. Both create a Proc object that can be stored in variables, passed to methods, or called later.

Parameters

ParameterTypeDefaultDescription
blockblockThe block of code to capture as a Proc object

Examples

Basic usage

my_proc = proc { |x| x * 2 }

my_proc.call(5)
# => 10

my_proc.call(21)
# => 42

Storing a proc in a variable

greeting = proc { |name| "Hello, #{name}!" }

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

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

Passing a proc to a method

def apply_operation(array, operation)
  array.map(&operation)
end

double = proc { |n| n * 2 }
apply_operation([1, 2, 3], double)
# => [2, 4, 6]

The & operator converts a Proc to a block. This is covered in more detail in the Common Patterns section.

Common Patterns

Converting between procs and blocks

Use & to convert a Proc to a block when passing to methods:

my_proc = proc { |x| x + 1 }

[1, 2, 3].map(&my_proc)
# => [2, 3, 4]

Use .to_proc on symbols for quick conversions:

[:upcase, :downcase, :reverse].map(&:to_s)
# => ["UPCASE", "downcase", "REVERSED"]

Proc vs Lambda

Procs and lambdas are both Proc objects, but they differ in argument handling:

my_proc = proc { |a, b| [a, b] }
my_lambda = lambda { |a, b| [a, b] }

my_proc.call(1, 2, 3)
# => [1, 2]  - extra args ignored

my_lambda.call(1, 2, 3)
# => ArgumentError (lambda is strict)

Lambdas return from their own scope, while procs return from the calling method:

def test_proc
  p = proc { return "returned from proc" }
  p.call
  "this won't print"
end

def test_lambda
  l = lambda { return "returned from lambda" }
  l.call
  "this will print"
end

test_proc
# => "returned from proc"

test_lambda
# => "this will print"

Using procs for configuration

Procs are excellent for configurable behavior:

class Calculator
  def initialize(&operation)
    @operation = operation
  end

  def calculate(values)
    values.map(&@operation)
  end
end

add_one = Calculator.new { |x| x + 1 }
add_one.calculate([1, 2, 3])
# => [2, 3, 4]

square = Calculator.new { |x| x ** 2 }
square.calculate([1, 2, 3])
# => [1, 4, 9]

Memoization with procs

def expensive_computation
  @cache ||= proc { heavy_calculation }
  @cache.call
end

def heavy_calculation
  puts "Computing..."
  42
end

expensive_computation
# Computing...
# => 42

expensive_computation
# => 42  (cached, no recomputation)

Errors

ArgumentError

Lambdas raise ArgumentError when passed the wrong number of arguments. Procs are lenient and assign nil to missing parameters or ignore extras:

strict = lambda { |a, b| a + b }
strict.call(1)
# => ArgumentError: wrong number of arguments (given 1, expected 2)

lenient = proc { |a, b| a + b }
lenient.call(1)
# => 1  (b is nil)

See Also

  • lambda — creates a lambda Proc with strict argument checking
  • block_given? — checks if a block was passed to the current method