Code Blocks in Ruby
Code blocks connected to a method call are called Ruby code blocks. Instances of the Proc class must be created whenever a program wants to handle these anonymous code segments as objects that can be passed around, stored, or returned.
Ruby provides two ways to describe code blocks as runnable, parameterized objects: Proc objects and Lambda objects. Custom control structures and functional programming approaches are made possible by these basic ideas.
Proc Objects (The Object Form of a Block)
Blocks are not objects; they are syntactic structures. A block can be converted into an instance of the Proc class, which is called a Proc object.
Creation and Conversion
The creation of procs can be done explicitly with Proc.new or implicitly by passing it into a method parameter that has an ampersand (&) before it:
Using Proc.new
: This is how a Proc class instance is typically created.
Using Kernel#proc
: This is a Ruby 1.9+ synonym for Proc.new. (Note: Lambda is the preferred term for method-like behavior in Ruby 1.8 instead of proc, which created ambiguity.)
Code Example: Creating a Proc
# 1. Using Proc.new to create a Proc object explicitly
p = Proc.new { |x, y| x + y }
puts p.class # => Proc
# 2. Passing a block to a method and capturing it with &
def get_proc(&block)
# The &block syntax converts the passed block into a Proc object
block
end
addition_proc = get_proc { |a, b| a + b }
puts addition_proc.call(5, 10) # => 15
Output
Proc
15
You can also read File I/O In Ruby: Basic Operations To Advanced File Handling
Lambda Objects
Although lambdas are likewise instances of the Proc class, their behavior is slightly different and more akin to that of a method than a block.
If you want your code to act as a formal method or function, lambdas are better than standard Procs.
Creation
Using Kernel#lambda
: This is the traditional way of making a lambda.
Using Arrow Syntax (->): In Ruby 1.9, an arrow (->)
was added to the literal syntax for lambdas, which is thought to be more elegant.
Code Example: Creating a Lambda
# 1. Using the lambda method
is_positive = lambda { |x| x > 0 }
puts is_positive.class # => Proc (Lambdas are a special type of Proc)
puts is_positive.call(5) # => true
# 2. Using the arrow syntax (->)
greeting = ->(name) { "Hello #{name}!" }
puts greeting.call("Sven") # => Hello Sven!
# Lambdas created via arrow syntax can include default arguments (Ruby 1.9+)
zoom = ->(x, y, factor=2) { [x * factor, y * factor] }
puts zoom.call(10, 5).inspect # => [20, 10]
Output
Proc
true
Hello Sven!
[20, 10]
You can also read What Is Command Line Crash Course Navigation In Ruby
Invocation
Since the Proc
class defines all of the methods needed to run the embedded code, many methods can be used to invoke both Procs and Lambdas:
- #call (The standard method).
- [] (The array access operator).
- .() (Ruby 1.9+ syntactic sugar).
Blocks as Closures
Lambdas are closures, as are Procs. Even if the variable bindings (local variables that were in scope when the object was defined) later leave scope, a closure still has access to them.
# Example illustrating closure (the lambda closes over the variable 'n')
def multiplier(n)
# 'n' is retained by the lambda, even after multiplier returns
lambda { |data| data.collect { |x| x * n } }
end
doubler = multiplier(2) # Sets n=2 in the closure
puts doubler.call([65-67]) # Prints 2,4,6
tripler = multiplier(3) # Sets n=3 in a separate closure
puts tripler.call([65-67]) # Prints 3,6,9
Output
-4
-6
You can also read What Is The Command Line Crash Course Manipulation in Ruby
Key Differences (Proc vs. Lambda)
Argument strictness (arity) and the return keyword’s effect are the two major traits that distinguish Procs (or “raw procs” defined via Proc.new) from Lambdas.
Argument Arity
Lambdas are strict (method-like): It is necessary to call them with the exact number of arguments that they were intended to accept. If this isn’t done, an ArgumentError
is raised.
Procs are flexible (block-like): They act similarly to blocks when called via yield
, ignoring extra arguments and assigning nil
to missing arguments.
# The complete code demonstrating argument handling differences
# 1. Lambda (STRICT Argument Count)
l = lambda {|x, y| x + y}
puts "--- Lambda (l) Examples ---"
puts "l.call(1, 2) => #{l.call(1, 2)}"
# l.call(1) # UNCOMMENT TO SEE: ArgumentError: wrong number of arguments (given 1, expected 2)
# l.call(1, 2, 3) # UNCOMMENT TO SEE: ArgumentError: wrong number of arguments (given 3, expected 2)
# 2. Proc (LAX Argument Count)
p = Proc.new {|x, y| x + y}
puts "\n--- Proc (p) Examples ---"
# Correct number of arguments
puts "p.call(1, 2) => #{p.call(1, 2)}"
# Too few arguments (y is nil, leads to TypeError: nil can't be coerced into Integer)
puts "p.call(1) => ERROR BELOW"
puts "---"
# This line is the one that produces the error you saw:
# p.call(1) # UNCOMMENT TO SEE: TypeError: nil can't be coerced into Integer (from 1 + nil)
# Too many arguments (extra arguments are ignored)
puts "p.call(1, 2, 3) => #{p.call(1, 2, 3)}"
Output
--- Lambda (l) Examples ---
l.call(1, 2) => 3
--- Proc (p) Examples ---
p.call(1, 2) => 3
p.call(1) => ERROR BELOW
---
p.call(1, 2, 3) => 3
You can also read Method Visibility In Ruby : Public, Private, And Protected
Return Behavior
The main functional distinction between a Lambda and a Proc is the impact of the return
statement in the former.
return
in a Proc causes a non-local return, meaning it attempts to exit the entire method that enclosed the Proc’s definition, not just the Proc itself. If the enclosing method has already completed, this raises aLocalJumpError
.return
in a Lambda causes a local return, meaning it returns control only from the lambda, just like a regular method call.
def test_proc
p = Proc.new { puts "In Proc"; return "Proc Result" }
puts "Before Proc Call"
p.call # Immediately exits test_proc method
puts "After Proc Call" # This line is never reached
end
def test_lambda
l = lambda { puts "In Lambda"; return "Lambda Result" }
puts "Before Lambda Call"
result = l.call # Returns only from the lambda, execution continues
puts "After Lambda Call: #{result}"
end
test_proc
# Output:
# Before Proc Call
# (Method exits, returning "Proc Result" to its caller)
test_lambda
Output
Before Proc Call
In Proc
Before Lambda Call
In Lambda
You can also read What Is Inheritance In Ruby With Practical Code Examples