Gunnari Auvinen's Blog

Blocks, and Procs, and Lambdas - Oh My!

June 22, 2014

Blocks, Procs, and Lambdas - Callable Objects

In addition to the wide library of method calls already found within Ruby, there is another powerful feature, which is the ability to create callable objects within your program. What exactly is a callable object you may ask? One of the primary callable objects within Ruby is the Proc object. In the Well Grounded Rubyist, David A. Black states that,

At its most straightforward, the notion of a callable object is embodied in Ruby through objects to which you can send the message call, with the expectation that some code associated with the objects will be executed. The main callable objects in Ruby are Proc objects, lambdas, and method objects. Proc objects are self-contained code sequences that you can create, store, pass around as method arguments, and, when you wish, execute with the call method.

There are lots of details that can be extracted from this information and will be explored over the following sections.

Procs

First up let’s discuss the Proc object and what it is exactly. Proc objects are units of code that can be passed around and then later executed with #call. One thing of note is that if you run #call on a Proc object, if there is a block of code associated with the object, it will be run. This is an incredibly useful feature and one of its applications is in closures, which will be discussed later in this post.

A new Proc object can be created by creating a new Proc class object and including a block of code. An example of that looks like this:

new_proc = Proc.new { puts "Inside new_proc's block."}  

Now the block of code that was passed in becomes the proc’s body, which can then be executed by calling the proc, like thus:

new_proc.call
# "Inside new_proc's block."

Now something else to note is that procs are Proc objects of the Proc class, which means that they can be passed around as any other object is in Ruby. The main difference is that a Proc stores a block of code that can be executed upon the proper call. Procs can be placed inside an array, assigned to a variable, stored in another object, and be used as a method argument to name a few things.

Blocks

Next I will talk about Ruby code block, which you’ve probably seen all over the place. Here’s an example of one in use.

2.times { |i| puts "#{i}. Hello, world!" }
# "0. Hello, world!"
# "1. Hello, world!"

In the code above the block is what is contained within the curly braces {}. Blocks can also be within the other syntax of do...end as well. The same code from above can also be written as:

2.times do |i|
  puts "#{i}. Hello, world!"
end
# "0. Hello, world!"
# "1. Hello, world!"

Having just seen rocs and blocks, you might think that they are the same thing, but in fact they are not! There are some subtle differences that must be discussed on how they differ though. Now, when a new Proc object is created, a code block must always be supplied, yet at the same time not all code blocks present in Ruby are there to create a proc. This may leave you scratching your head, so here’s an example to help sort out some of the confusion!

[1,2,3].map! { |elem| puts elem.to_s }

The coded block being passed into #map! isn’t a proc, yet it would be easy to turn it into one.

Another difference between a Ruby code block and an instance of Proc is that the block isn’t an object, while the instance of Proc is one. The Ruby block is instead part of the syntax for the receiver method call. Looking at the first example above the block, { |i| puts "#{i}. Hello, world!" }, is not an object itself, it is instead part of the #times method input syntax.

Lambdas

The next piece to discuss here is lambda. Lambda objects are created the same way as a new Proc object. The interesting part about this is that when a new lambda object is created, it is actually a new Proc object with a special lambda notation.

Now you may ask, why do we need lambdas if they are just Procs? Well lambdas are different in several distinct ways. The first way they are different from regular Procs, is that they must be created specifically, as in a lambda will not get created unless it is specified. Another key difference between Procs and lambdas, is that lambdas check the number of input arguments and they must be passed the proper number of arguments. For example if a lambda were expecting one argument and was passed zero arguments, it would raise an ArgumentError.

The final difference between a lambda and a regular Proc, is that they treat the return keyword differently. A return within a lambda will exit out of the body of the lambda, while a return within a proc will return to the method from which the proc was executed. These different behaviors are illustrated in an example below:

def test_code
  lambda_return = lambda { return }
  lambda_return.call
  puts "lambda_return has been called and this is still within the test_code method."
  proc_return = Proc.new { return }
  proc_return.call
  puts "proc_return has been called, therefore this will never be seen."
end
test_code
# "lambda_return has been called and this is still within the test_code method."

The Glory of Closures!

After learning about procs, blocks, and lambdas, the next thing to do is to talk one of their most powerful uses, which is a closure. A closure is an anonymous function, one that doesn’t have a name, that maintains the local variable bindings that were in effect when the Proc was created. This is an incredibly useful feature, as the closure will keep part of the running state of the program.

One wonderful example of closures in Ruby is that of a counter as seen below:

def closure_counter
  start = 0
  return Proc.new { start += 1}
end
new_counter = closure_counter
3.times { puts new_counter.call }
# 1
# 2
# 3
second_counter = closure_counter
second_counter.call # 1
new_counter.call    # 4

This example demonstrates how each closure keeps the local variable bindings that were in effect when the Proc was created. This can be seen by the output of both new_counter and second_counter. new_counter outputs 1, then 2, and then 3, at which point second_counter is called and outputs 1, because the start that was the local variable to that proc is different than start used for newcounter. This is evidenced in the last line where newcounter outputs 4, and second_counter is still at 1.

Closing Thoughts

There are so many uses for blocks, procs, and lambdas. They can be used as individual pieces or they can be used as closures to provide a powerful tool to any developer. They will aid in the creation of simple and complex programs and I am very much looking forward to using them more in the near future.

Additional Materials

This post scratches the surface of the interesting Ruby world of blocks, procs, and lambdas. One resource I found particularly helpful when examining these topics was The Well Grounded Rubyist 2nd Edition, by David Black. Specifically chapter 14 relates directly to the topics discussed here.


Gunnari Auvinen

Written by Gunnari Auvinen who works at Turo. The opinions expressed here are his own. You should follow him on Medium.