When you start write in Ruby, sooner or later (rather sooner), you will meet with the concept of Proc and blocks. Especially the last one is something really popular in Ruby, so it's good to know what is this and why is it so useful. I think that there are many sources that can explain the concept of blocks, therefore I'm not going to write another article about basics of the blocks. For me the more interesting is the differences between Proc and blocks and how is it possible that you can write some methods like this:

def my_method(&block)
  block.call   
end

What happens when you execute such methods and why is this possible to give it as an argument block or Proc interchangeably? These are question that I will try to answer today.

Proc class

Let's do quick reminder: Proc is a class. To create and instance of that class, we call method new and as an argument we give block:

my_proc = Proc.new { puts "I'm Proc object" }

Exactly the same effect you get when you use proc method, since the method returns the instance of Proc object:

my_proc = proc   { puts "I'm Proc object" }

Now, when we have this instance of the Proc object we can send it the call message:

my_proc.call #=> "I'm Proc object"

This caused that the block was invoked. So you can imagine the Proc object as a box in which you can preserve block of code and exetute it later.

The differences between blocks and Proc'a

So we already know that Proc is a class and we can create an instance of it. Block on the other hand is not an object. We can't create any instance of it and we are not able to send any message to it. Moreover, we can't send it as an argument to a method, since methods can only take objects as arguments. Wait a minute. So how is it possible that we can call such method ?

['a','b','c'].map { |letter| letter.upcase }

Interesting, isn't it ? Let's investigate how the signature of the method looks like.

def map(&block)
end

Here we are our misterious operator &. It looks like this additional sign before argument causes that this is possible to send block as an argument, but how exactly does this work ?

&block

Let's start with an example of method:

def simple(&block)
  block.call
end  

simple { puts "I'm just a block" }

What really happens here is that Ruby in background triggers to_proc method on the block. As a results we get Proc instance. So inside method we do not operate on block any more, but we have Proc instance for our disposal. That's why we can send call message to block argument.

But this is not the whole story yet. In real world, our simple method can also take Proc instance as an argument as well.

my_proc = Proc.new { puts "I'm a proc" }   
simple(&my_proc)

In this case we use & operator in method calling and it's job is quite different here. simple method is waiting for a block, not a Proc instance. So in this case & operator does two jobs:

  • tells simple method that proc instance is serving as a block
  • calls to_proc method on my_proc. In case of Proc instance, this method just returns the instance itself.

&:upcase

Let's return to our previoud example:

['a','b','c'].map { |letter| letter.upcase }

We can get the same effect if we write:

['a','b','c'].map { |letter| letter.send(:upcase) }

or even more elegant:

['a','b','c'].map(&:upcase)

We just know that & operator is a wrapper for to_proc method. In this case the to_proc message is send to symbol, in our case this is :upcase. So let's see how the implementation of to_proc method migth look like, keeping in mind that this method should return a Proc instance.

class Symbol
  def to_proc
    Proc.new { |object| object.send(self) } 
  end
end

So simple, isn't it? :)

yield

Using yield statement is one another way to call a block. So we can create such method:

def simple
  yield
end

Then we can call it with any block:

simple { puts "Let's keep it simple" }

The rule here are the same. If we'd like to pass an Proc instance to the method, we have to remember about &, to tell method that we want to use Proc as a block.

my_proc = proc { puts "Let's keep it simple" }
simple &my_proc

Faster Rails applications with browser caching

Web application performance is one of the most important topic nowadays. Most of us create application that have to be fast not only on d...… Continue reading