Closures in Ruby

Some time ago, as a dyed-in-the-wool C++ programmer who was exploring the mysterious world of Ruby, I got enthralled by the concept of closures and the way the language handled them. I was already familiar with the concept of closures from my JavaScript experience, but the manner in which Ruby handles closures both perplexed and fascinated me. Years later, I decided to write an article about them and here it is.

What’s a Closure?

A closure is basically a function that:

Can be treated like a variable, i.e. assigned to another variable, passed as a method argument, etc.

Remembers the values of all the variables that were in scope when the function was defined and is able to access these variables even if it is executed in a different scope.

Put differently, a closure is a first-class function that has lexical scope.

Code Blocks

A block is, in effect, an anonymous function that also offers closure-like functionality. Consider the following,

outer = 1
def m
inner = 99
puts "inner var = #{inner}"
end

Unlike some other languages, Ruby doesn’t nest scopes, so the inner and outer variables are totally shielded from each other. What if we want to access the inner variable from the outer (a.k.a main) scope? One way of doing it is by using a block:

From within the method, we’re yielding the inner variable to a block that’s appended to our method call. We then use the inner variable value to do some arithmetic in our main scope. Althoh the code block is, effectively, an anonymous function, it can still access the variables in its surrounding scope, such as ‘outer’, while accessing variables within the method that yields it. In other words, using a block like that allows us to cross a scope gate.

“Why Can’t I Use the Method’s Return Value Instead?”

In this simple example, you could. However, if you want to return more than one variables, you fail, while you can easily yield multiple variables to a block. More importantly, the method won’t have access to the surrounding variables at the point of its definition, so you won’t be able to do the cool things we’ll see in the rest of this article :)

Procs

You may have noticed that our code block example didn’t really fulfill the criteria we defined for a closure. While the block remembers variables in its surrounding scope and can access variables yielded to it from a different scope, we can’t pass it as a method argument or assign it to another object. In other words, blocks used with the yield statement are not true closures. Blocks can be true closures but they have to be treated as Procs. Let’s look at how we can do just that:

The first difference between this and our previous example is that we’re now defining a parameter for our method: &a_block. This tells the method to expect a block as an argument and also treat it like a Proc object (the & operator implicitly converts the block to a Proc). A Proc is simple a named code block, meaning, an actual object. Since it is an object, the block can be passed around and have methods invoked on it. The method of interest here is #call, which invokes the Proc. Knowing all this, we can rewrite our code as follows:

Here, we’re creating a Proc object on the fly and passing it as a method argument. To fully leverage a closure, we need to be able to assign it to another variable and call it when we need it (deferred execution). Let’s modify our method to achieve just that:

Our method now receives the outer variable as an argument and returns the Proc that does the addition of inner and outer. We then assign the Proc to a variable (p), called at our leisure further down in the code. Note that, even when we change the value of outer before the proc call and set it to 0, our result isn’t affected. The Proc remembers the value of outer when it was defined, not when it was called. We now have a real born closure!

Lambdas

Time for a spot-the-difference game. Take a look at the following code and see how it differs from the code in the previous section:

Yes, the only difference is that our method now returns a lambda instead of a proc. The functionality and output of our code remain exactly the same. But…hang on, we assigned a lambda to p, butp tells us it’s a Proc! How’s that possible? The answer is: that lambda is but a Proc in disguise. #lambda is a Kernel method that creates a Proc object which behaves slightly differently to other Proc objects.

“How Can I Tell If an Object is a proc or a lambda?”

Just ask it by calling its #lambda? method.

obj = lambda {"hello"}.
puts obj.lambda?
#=> true

Also, calling #inspect on a lambda will give you its class as a Proc(lambda).

Procs vs Lambdas

We already saw that a lambda is just a Proc with some different behavior. The differences between a Proc (proc) and a lambda encompass:

A block created with lambda returns back to its parent scope just like methods do, so no surprises there.

A block created with proc (or Proc.new) thinks it’s part of its calling method, so returns back to its calling method’s parent scope. Which can be a bit shocking when you realize half of your method’s code wasn’t executed because the Proc you put half-way through it had a return statement. So, can we return from a Proc the “normal” way? Yes, if we use the next keyword, instead of return. We can re-]write method_b so that it’s functionally the same to method_a:

We can see that the proc is much more chill about its arguments. If an argument’s missing, the proc will simply assume it’s nil and get on with life.

Syntactic Sugar

One of the many things I love about Ruby is that it allows us to do the same thing in many different ways. Think that the #call method is too Fortran-like for your tastes? No problem, call your proc with a dot or double-colon. Don’t like that either? There’s always the square bracket notation.

Here, the #map method expects a block, but we can easily pass it a proc or a lambda instead. This means we can utilize the power of closures to transpose variables from other scopes into the goodness of Ruby’s block-expecting methods, which is pretty powerful stuff.

So What’s the Big Deal?

Ruby offers unrivaled versatility in implementing closures. Blocks, procs, and lambdas can be used interchangeably to great effect. Much of the “magic” created by many of our favorite gems is facilitated by closures. Closures allow us to abstract our code in ways that make it smaller, tighter, re-usable and elegant 2. Ruby empowers developers with these wonderful and flexible constructs so that we can utilize closure power to the max.

Fred is a software jack of all trades, having worked at every stage of the software development life-cycle. He loves: solving tricky problems, Ruby, Agile methods, meta-programming, Behaviour-Driven Development, the semantic web. Fred works as a freelance developer, and consultant, speaks at conferences and blogs here .