closures
« Optimizing your mootools application using Sizzle! | virtualenv + virtualenvwrapper »
I will take some minutes to post about closures before starting to work :)
What are closures?
Closures are a pretty neat feature you can find in languages like Scheme, Lisp, Ruby, JavaScript and Python (among others).
Closures can many times increase your code readability and cleverness. You'll find examples of use the codes samples below.
To understand what a closure is you need to understand the following concepts:
- first-class objects: objects that can be stored in a variable, passed as argument and/or returned by a function.
- first-class functions: if all the characteristics of the first-class objects apply to a function then it's called first-class function. This is a particularity of the programming language itself and is not up to the programmer to set this. If you come from Java, C, PHP you'll probably find some enlightment regarding closures in this article since in those languages functions are not first-class objects (c'mon don't come up with crazy pointer stuff).
- free variable: is a variable that can be accessed from a function but this variable is not an argument or a local variable of that function.
- lexical binding: all variables are contained in their respective environment (code block) instead of being available globally (dynamic binding).
With the definitions above, then we can define a closure as:
A lexical binded first class function which can access free variables.
I know that line probably didn't helped that much. So let's go to the...
Code examples
The most common example for a closure seems to be a counter, so lets do it with Python:
def counter(start):
# we are setting the variable start as a counter function property
# because if we don't do that `start` will be placed in the local
# scope of incr after incrementing its value.
counter.start = start
def incr():
counter.start += 1
return counter.start
return incr
# this binds the closure function incr to the c variable so we can
# call it later. Of course, before that, this will also set the
# counter.start variable to 10 but nothing else will happen as desired
c = counter(10)
print c() # will print 11
print c() # will print 12
print c() # will print 13
Here is a better real life example in JavaScript.
Let's say we want to print out the order of the links contained in a list when they are clicked (yes, I'm using jQuery in the example ;).
The first thing you'd probably try is:
var links = $('li a'); // gets all <a> elements contained in a <li>
for(var index=0; index < links.length; index++) {
// $() extends the element so the click function is available.
// $().click() allows us to bind the onClick event.
$(links[index]).click(function(event) {
alert(index);
return false
});
}
This will fail! After you click an element it will print the value of index, but its value will be the latest it got in the for loop. In this case, we have a closure (the index variable is accessed via lexical binding) but is not working as expected.
The solution for this would be rebinding each value of index in an inner lexical scope and then add the closure inside that scope. This way, the closure will be looking to that inner value of index instead of the latest state of the for loop.
var links = $('li a');
var closure = function(i) {
return function(event) {
alert(i);
}
}
for(var i=0; i < links.length; i++) {
event_handler = closure(i);
$(links[i]).click(event_handler);
}
And there we go! Now it's working as we wished. However that was just for the sake of a detailed example. The best way of doing it with jQuery will be using each like this:
var links = $('li a');
links.each(function(index) {
$(this).click(function(event) {
alert(index);
return false
});
})
Which remains on-topic because the index is accessed by the click event handler thanks to lexical binding.
Conclusions
Closures are a great language feature, they can be used for many purposes (even for a kind of poor man's object oriented programming). I'd recommend you to play with them a little to understand them better.