This is the last part of a three-part series where we take a close look at Code Block, Proc, Lambda, and Closure in Ruby.
In Part 1, we covered:
- Definitions of Code Block, Proc, Lambda, and Closure
- Constructions
- Calling a Code Block/Proc/Lambda
- Passing and Returning Procs
In Part 2, we covered:
- Scopes, Universes, and Lunch Boxes
- Differences between Procs and Lambdas
In this post, the last part of the series, we will discuss:
Proc <> Code Block Conversion and Ampersand(&
)
- A Code Block VS An Argument In the Context of A Method
- Behind the Scenes of
[‘hello’, ‘world’].map(&:upcase)
Let’s dive into it!
Proc <> Code Block Conversion and Ampersand(&)
Hold on! Before we jump into this topic, let’s briefly revisit some foundations covered in Part 1:
- A
proc
is an object that contains acode block
so that thecode block
can be executed later - A
proc
, similar to all other objects, can be passed into a method as an argument, and a method executes a code block inside a proc by calling the proc. - A
code block
can be attached to a method directly, and a method executes the code block by yielding to it.
Now, here are the questions:
- can we attach a
code block
to a method and call it later? - can we pass a
proc
into a method and yield to it later?
The answers are no and no.
Because you call a proc and you yield to a code block.
BUT, we can:
- attach a code block to a method, convert it to a proc, and call the proc later
- pass a proc into a method, convert it to a code block, and yield to it later
The conversion between a proc and a code block happens with the help of ampersand(&
).
Let’s look at an example:
In the code above, we clearly do pass in a code block
into the method. We also indeed call the “block” inside the method. What actually happens, thanks to the ampersand(&), is that the passed-in block
gets converted to a proc
.
You can think of the ampersand as a magic wand: when it’s prepended to an argument of a method, it converts the appended code block
to a proc
and assigns the result to the argument name.
In the example, the appended code block
is converted to a proc
, and the resulting proc
gets assigned to the variable named “block”.
The other way works as well:
If an ampersand is prepended to a proc
, it converts the proc
into a code block
and appends the code block
to the method being called.
In line 6, we prepend an ampersand (&
) to the proc
, which gets converted into a code block
, and append the resulting code block
to the method. The method later yields to the code block
.
A Code Block vs An Argument In the Context of A Method.
There’s one thing worth emphasizing: the ampersand converts a proc
into a code block
and appends the code block
to the method, NOT passing the code block
in as an argument to the method.
Appending a code block
to a method and passing an argument to a method are TWO DIFFERENT THINGS. Although they can both be used by the method, they are two different things.
They are as different as how a jacket is different than a pair of pants. You can wear a jack. You can wear a pair of pants. You can wear a jack and a pair of pants at the same time. You can use the material of a pair of pants, convert it into a jacket, and wear it. But wearing a jacket converted from a pair of pants is not the same as wearing a pair of pants!
You can append a code block
to a method and passing arguments to the method at the same time. But appending a code block
to a method is different than passing an argument to a method.
What happens when you confuse a code block as an argument? irb will yell at you:
The give_me_another_block
method is expecting an argument. But with the ampersand prepended to a proc
, we convert the proc
to a code block
and append it to the method. Although it looks like we pass an argument to the method, we actually append a code block
to the method without passing any arguments in. That’s why irb gives us the wrong number of arguments method.
Now we pass the method an argument and also append a block to it. The same as you wearing a jacket and a pair of pants at the same time!
The “magical” ampersand (&)
Enough fun with jackets and pants! Let’s switch our attention to the “magical” ampersand (&
): what exactly is happening?
When we do &pr
we are actually doing pr.to_proc.call
:
- we first call the
to_proc
method onpr
, - then we call the
proc
returned byto_proc
.
The magic wand ampersand(&
) is nothing but another ruby syntactic sugar.
Does &pr
remind you of any old friends? like .map(&:upcase)
? What are their relationships? Let’s think about that for a second …

The answer is, in .map(&:upcase)
, the ampersand(&
) is doing exactly the same thing as it does for &pr
: it calls pr.to_proc.call
.
Consider [‘Hi’, ‘Yo’].map(&:upcase)
.
On one hand, we know it is the same as:
[‘Hi’, ‘Yo’].map { |element| element.upcase }
, which is the same as[‘Hi’, ‘Yo’].map { |element| element.send(:upcase) }
On the other hand, we know &:upcase
is the same as :upcase.to_proc.call
So [‘Hi’, ‘Yo’].map(&:upcase)
is the same as:
[‘Hi’, ‘Yo’].map { |element| :upcase.to_proc.call(element) }

How does [‘Hi’, ‘Yo’].map {|element| :upcase.to_proc.call(element) }
becomes [‘Hi’, ‘Yo’].map { |element| element.send(:upcase) }
??
Well, the key lies in the definition of to_proc
in the Symbol class.
And I will leave it as a homework for you.
Share your answer in the comment section. 😀
Thanks for reading.
If you find this article helpful, please pass it around.?
Don't forget to subscribe!
Great explanation for a confused topic!. Thanks so much.
Glad you find it helpful!
Thanks for the awesome series. I now understand blocks, procs and lambdas in Ruby.
to_proc is probably defined like this in Symbol class.
def to_proc
proc { |element| element.send(self) }
end
WOWOW!! This was great !!