loading...

Understanding proc objects

abeidahmed profile image Abeid Ahmed ・3 min read

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

Posted on by:

abeidahmed profile

Abeid Ahmed

@abeidahmed

Self-taught programmer. Javascript and ruby enthusiast. Life long learner!

Discussion

markdown guide
 

Awesome…so many things you can do with Ruby blocks & procs (and lambdas). Love it!