What is a proc?
Proc objects are blocks of code that have been bound to a set of local variables. That means we can pass them as method arguments, push them inside an array, and do any other things that we normally do to objects.
Before moving on, I would like to clarify that Proc
will denote the class and proc
will denote the instance of the Proc
class.
Calling a proc
pr = Proc.new { puts "hello world, once" }
pr.call
# hello world, once
Proc.new
is an alias for the Kernel#proc
method.
pr = Kernel.proc { puts "hello world, twice" }
pr.call
# hello world, twice
We can also write like this.
pr = proc { puts "hello world, thrice" }
pr.call
# hello world, thrice
Capturing a code block
A method can capture a block and objectify it using the special &
operator. Without the &
operator, ruby has no way of understanding that you want to perform a block to proc conversion, rather than passing a regular argument.
def call_a_proc(&block)
block.call
end
call_a_proc { puts "Hello world" }
# Hello world
Similar to this we can also place a Proc.new
to serve in place of the code block.
p = Proc.new { |x| puts x.upcase }
%w(abeid ahmed).each(&p)
It outputs to:
ABEID
AHMED
=> ["abeid", "ahmed"]
Here p
is an instance of the Proc
class. You can verify this using the p.class
in your terminal.
A Proc.new
takes in a code block that tells what to do when a proc
is called. Now that is exactly what we are doing in the above code.
If you are writing ruby for sometime, you must be familiar with the each
keyword. each
method, takes in a block of some kind with an argument. The &p
convinces each
that it doesn't need an actual block and to receive a proc
object.
The above code is same as saying:
%w(abeid ahmed).each { |x| puts x.upcase }
Block-proc conversion
def capture_block(&block)
block.call
end
capture_block { puts "Beautiful day" }
We've seen this type of code in one of the examples above. So, here's how this method gets called.
Step 1
The capture_block
method with the code block is called first.
capture_block { puts "Beautiful day" }
Step 2
A new proc
object is created using the Proc.new
.
Proc.new { puts "Beautiful day" }
Step 3
The proc
created in step 2, is captured or bound to the block
argument in the capture_block
method.
capture_block(&block)
So, these are the three steps that are completed to call the capture_block
method.
Wait, there's more to this proc
. Keen readers would be able to identify it and even if you didn't it is completely fine.
Remember the each
example that we talked about? We passed in a Proc.new
, like,
%w(abeid ahmed).each(&p)
We can do the same with the capture_block
method. Instead of calling the block like,
capture_block { puts "hello world" }
we can do exactly the same as the each
method. First we create a new Proc.new
and we pass it on.
my_proc = Proc.new { puts "Another way" }
capture_block(&my_proc)
# Another way
Conclusion
In this article we have accomplished quite a lot. We learned how to create proc
objects, how to capture them, and how the conversion from block
to proc
takes place. If you are interested in learning more about Proc
head over to the official ruby docs.
This is my first article on this platform, so go easy on me 😄. If you found this article helpful, you know the drill and if there's any typo or general errors let me know in the comments. Good day and happy learning!
Credits: The Well Grounded Rubyist
Top comments (1)
Excelent article!!