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_procmethod on the block. - It marks that block as a proc object and can be executed using the
callmethod inside thecapture_blockmethod.
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_blockmethod 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
returnmethod. If areturnstatement is executed inside a lambda, it will return out of that lambda to the calling context. Whereas, if areturnis 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.