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. In capture_block(&block)
, the &
has two purposes:
- It converts the block into a proc object by triggering the
to_proc
method on the block. - It marks that block as a proc object and can be executed using the
call
method inside thecapture_block
method.
We cannot call capture_block
like capture_block(p)
or capture_block(p.to_proc)
with p
being a Proc because in these cases, Ruby interprets it as a regular parameter, and you cannot trigger the proc call inside the method.
Additionally, a Proc
can also be called within a code block using &
.
Example:
proc = Proc.new {|x| puts x.upcase }
%w{ ruby and coffee }.each(&proc)
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!
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 thecapture_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.Lambda and proc differ in how they handle the
return
method. If areturn
statement is executed inside a lambda, it will return out of that lambda to the calling context. Whereas, if areturn
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!"
p = Proc.new { return }
p.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 (0 for 1)
lam.call(1,2,3)
ArgumentError: wrong number of arguments (3 for 1)
proc = proc {|x| p x }
=> #<Proc:0x42ee9c@(irb):21 (lambda)>
proc.call(1)
1
=> 1
proc.call
nil
=> nil
proc.call(1,2,3)
1
=> 1
In summary, lambda follows stricter logical reasoning. Meanwhile, proc is more lenient.
Public comments are closed, but I love hearing from readers. Feel free to contact me with your thoughts.