ghammell'sBlog

Blocks, Procs, and Lambdas

Blocks, Procs, and Lambdas are powerful aspects of the Ruby language. Although they are similar in many ways, depending on your objective, it might make sense to choose one over the others. So, what are each of them and how do they differ?

Blocks & Procs

Lets say that we have an array of numbers that we want to adjust in several ways:

nums = [1,2,3,4,5,6,7,8,9,10]

We might add 2 to every number by typing something like this:

p nums.map! {|num| num + 2}

[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

What did we do there, exactly? Well, the map! method knows to iterate through each object within the nums array. For each object in the array, the map! method executes the instructions passed to it in the block of code to the right (within the { }). This block of code takes each object in the array as an input, assigns it the temporary variable name 'num', and then changes that num by adding 2.

What's important to take away here, though, is that the instructions we passed to the map! method were in a block. A ruby block is set of code that can be executed, and is one of the few items in ruby that is not considered an object.

But what if our plan was to increment this array several times? We might get tired of continually typing

p nums.map! {|num| num + 2}

everytime we wanted to increment the array. Instead, we could save those instructions as a Proc that we can access by name. A proc is essentially just a block of code that is saved with a variable name to be accessed later. We could establish a Proc that will increment our array like this:

add_2 = Proc.new {|num| num + 2}

And now if we want to increment the array 3 times, we can just write:

p nums.map!(&add_2)
p nums.map!(&add_2)
p nums.map!(&add_2)

[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
[7, 8, 9, 10, 11, 12, 13, 14, 15, 16]

By saving our set of instructions used to increment as a Proc, we can now just use the name of that proc to access the instructions, instead of typing the block over and over again. This becomes more useful as the complexity of the block increases. For example, we might create another proc that will tell us whether the numbers passed in are cold, warm or hot. This code is a bit longer, so it would make sense to store it as a Proc if the intent was to use this same functionality several times:

temp = Proc.new {|num| num > 12 ? "hot" : num.between?(9,12) ? "warm" : "cold"}

p nums.map(&temp)

["cold", "cold", "warm", "warm", "warm", "warm", "hot", "hot", "hot", "hot"]

p nums.map!(&add_2)
p nums.map(&temp)

[9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
["warm", "warm", "warm", "warm", "hot", "hot", "hot", "hot", "hot", "hot"]

One thing to keep in mind is that in order to access the functionality of a proc when passing it to a method, you need to include the '&' in front of the proc name. This basically tells the method that you want to access the proc's functionality, not just the proc itself as an object.

As you can see, the use of Procs not only saves you time, but also prevents unnecessary repetition and can help make your code even easier to read.

Procs vs. Lambdas

A lambda is another way of storing a block of code, and only differs from Procs slightly. The two major differences between a lambda and a Proc are that:

To illustrate the second point, lets look at a method that utilizes a lambda:

def greet(person)
--greeting = lambda {return "Hey there #{person}!! So great to talk to you!"}
--puts greeting.call

--"How are things going?"
end

puts greet("craig")

Hey there craig!! So great to talk to you!
How are things going?

Everything looks good, right? What if we replaced our lambda with a proc? Like so:

def greet(person)
--greeting = Proc.new {return "Hey there #{person}!! So great to talk to you!"}
--puts greeting.call

--"How are things going?"
end

puts greet("craig")

Hey there craig!! So great to talk to you!

Notice how only the string within the Proc was returned, and not the second one like when we used a lambda? That's because when returning out of a Proc, the method from which we accessed that Proc is broken out of. Returning out of a lambda takes us right back to where we were within the method.

As you can see, blocks, Procs, and lambdas are very powerful. Which one you use at which point in time will be dictated by the functionality you are trying to build, and whether you'll want to re-use that functionality several times over.

Thanks for reading!


Copyright: Gary Hammell 2014