An Introduction to Closures in Ruby
September 6th, 2007
More experimenting with closures and iteration led to some interesting results. Due to some quirks with Ruby's scope rules there are a few gotcha's and some of my initial assumptions about how variables are bound were wrong. Hopefully this entry will serve to enlighten those who, like me, wondered about the mystical Ruby closure.
What are closures?
Closures are an stateful object created on the fly when a block of code with its own scope becomes bound to variables in its environment. Closures consist of two elements. The first is a reference to some executable code such a block, proc, or method. The second is the set of variables that are bound to the scope of the executable block. This code creates a closure bound to c: n = 2; c = lambda { n * n }; A closure is created any time a lambda references variables not local in scope. To be perfectly clear a closure is loosely:
struct Closure {
void *function;
void *function_arguments;
};
What problem do closures solve?
Closures are extremely useful for implementing callbacks in event driven systems. Most of us are familiar with Javascript's event handlers that store a function as a first class value to be called at the appropriate time. Creating a reference to a function stores not only the equivalent of a function pointer in C, but also any variables referenced by that function. This provides a mechanism to automatically restore the function's environment whenever it is called. In C this would be accomplished with an additional parameter on the callback function for state data which would be initialized when when the callback was registered. For example, several buttons might have the same callback function but to know which one was pressed the callback would need an additional parameter. If the library implementing the callback didn't provide a parameter for state one would need to register different callbacks for each button and then call the common function after some initialization. Callbacks in an object oriented language are often implemented by registering a stateful object and using setters (obj.meth = val) to explicitly initialize the environment that will exists when a particular callback method is invoked. Closures usually provide a much more elegant solution by binding the environment implicitly.
How do closures capture state?
The following code fills an array with closures that bind n from a block passed to an iterator. There are three nested scopes which I will refer to as outer, iteration block, and lambda.
- the outer in which closures is defined
- the iteration block in which n is defined
- the lambda in which n is bound to the lambda
closures = []
(0..7).each { |n|
closures << lambda { n }
}
# Closures are bound to their individual copy of n.
n => NameError: undefined local variable or method ...
closures.map { |c| c.call } => [0, 1, 2, 3, 4, 5, 6, 7]
n = 3
closures.map { |c| c.call } => [0, 1, 2, 3, 4, 5, 6, 7]
From the above it is apparent that each lambda holds its own copy of n since n is both distinct and unaffected by changes to the outer n. This happens because the iteration block n goes out of scope after each pass leaving only a lambda scope binding to n. Each successive pass through the iteration block yields a newly scoped n that does not affect previously bound lambdas. Each closure has saved its own copy of state.
How does scope affect the closure's binding to n?
The next piece of code is the same as the first with one change; n is defined in the outer scope. The lambdas behave very differently as a result.
# Here we introduce n in the outer scope
n = 0
closures = []
(0..7).each { |n|
closures << lambda { n }
}
# Closures are bound to the outer n
n => 7
closures.map { |c| c.call } => [7, 7, 7, 7, 7, 7, 7, 7]
n = 3
closures.map { |c| c.call } => [3, 3, 3, 3, 3, 3, 3, 3]
Now, since n was defined in the outer scope, each iteration only modified the value of the outer n. Closures are therefore bound to the same n instead of their own copy, and changing n changes the n in every closure. The first example, where each lambda bound its own n, is analogous to instance variable state. The second example, where there is only one copy of n, behaves more like a static class variable in some other languages.
Look out for the for loop!
Ruby's for loop does not have its own scope. It brings a variable into scope on the first pass and leaves a reference hanging around for later. The following code has only two scopes; outer and lambda. n is introduced into the outer scope on the first iteration and remains defined even after the loop terminates, despite its being introduced as part of the iteration construct.
closures = []
for n in 0..7
closures << lambda { n }
end
n => 7
closures.map { |c| c.call } => [7, 7, 7, 7, 7, 7, 7, 7]
How to learn more
I am convinced that the only way to learn to program is to make something that doesn't work and then fix it. To understand closures it is necessary to understand the problem they solve. If you're like me and you read too much and program too little it's no wonder many things seem out of reach. At least I still get a spiritual feeling of enlightenment and enthusiasm as a result of finally beginning to understand. This gospel compelled me to write and so I have.
on January 29th, 2008 at 09:54 AM
I am becoming obsessed with Ruby. I appreciated your efforts. I coded your examples 1 by 1 and tried slight variations, good stuff. I am also going through this file, this guy went all out: http://innig.net/software/ruby/closures-in-ruby.rb
on October 7th, 2009 at 08:20 PM
Please, I cannot read an article where the author spells “its” with an apostrophe…
;-(
on October 23rd, 2009 at 11:53 AM
Excellent job on the introduction to Closures. Its writing was well done and simple to follow. It’s going to be useful to many people.
See the difference there with the “Its”? They mean 2 separate things.
on November 26th, 2009 at 06:22 PM
This will be solved in ruby 1.9, where blocks will have local variables.
on December 16th, 2009 at 01:10 PM
It’s fixed.
on February 1st, 2011 at 08:59 PM
thanks! good/simple explanation! @fred did that really need correcting b/c you’re anal?
on May 5th, 2011 at 09:13 AM
Thanks for the post! I’ve also written an article about Ruby blocks and closures with code examples.
on October 27th, 2011 at 04:08 AM
Really like your blog,thanks for sharing it with us.And I also like beats by dr dre.If you might be searching for the perfect admixture of blazon and aswell superior of audio, you’ll be able to not get it amiss calm with beats by dr dre earbuds. It’s the trusted choice of tunes fanatics outside of the planet. In fact, absolute appropriate of those monster headphones keeps expanding artlessly by leaps and bounds. Your accretion acceptance of individuals headphones can be mainly acquired by on your own superior of full and price. Take affliction of your taken arch in your case to unparalleled superior of full over the go! monster presents categorical an enviable alcove for by itself. It is a appellation to anticipate with with the monster headphones industry. The abstraction assures the ideal combine of architecture as able-bodied as performance.
on November 4th, 2011 at 05:06 AM
Really like your blog,thanks for sharing it with us.And I also like beats by dr dre.Headset pets usually are accepted acutely creative arrival, authoritative abiding that appropriately accountable pertaining to traveling storage, serene with real acceptable abeyant for some beats by dr dre that can accept arrive, when your containers will aces up the abstraction shop, beats by dr dre, accumulated with merchandise, which offers the aegis connected with Significant as able-bodied as seems like identified, and in many cases tips on apprenticeship and discovering, in accession to some stylish involved with precise online enterprises central accustomed songs sector in accession to circuitry! Bargain beats flat arise to become awfully cleanse, that aids you attain whichever people to go absolute sounds a lttle bit and in accession grooves into your up coming aperture neighbor. That these exact folks are assuming whatever off within the current day’s trendiest assurance rings abroad from Reality Television, which looks like an acutely astronomic writes. Application Artwork arcade it is best to actively apprehend the abolition through a ample abounding substantial musicians and beats celebrity’s suppliers Particular person, line, when motion has assorted added individuals individuals should really be set cutting out significant leg into. This can be in fact absorbing and states distinct.
on February 23rd, 2012 at 08:08 AM
Please remove the spam comments from “beats by dr dre”.
You wrote “The second is the set of variables that are bound to the scope of the executable block.” This is not exactly true. The point of the closure is that it carries state outside the scope of the block. In other words, the closure carries the state bound in the current stack frame which includes method parameters, local variables and self, which is always defined in Ruby.