Closure in Javascript, Ruby and Python
Closure is after an outer function returns, when inner function is called, it still has access to the outer function's defined variables.
In order for the language to be able to support closures, it must support first-class functions. A first class function is a function that can be treated like an object. Javascript by nature supports first-class functions. Let's start from Javascript closure.
Javascript Closure
var printer = function(){
var string_to_print = 'some string';
return function() {
console.log(string_to_print);
}
}
var print = printer();
print()
In console, it logs:
> 'some string'
This printer function returns a reference to the child function (anonymous) contained within it. When this child function (nested function) is invoked, it still has access to printer function's scope, i.e. read the variable defined in printer function.
The inner function can not only read the outer function's variables but can also write. The following is a making counter example, each time the function gets called, counter goes up by 1.
function make_counter(){
var counter = 0;
return function(){
return counter++;
}
}
var count = make_counter();
console.log(count());
console.log(count());
console.log(count());
make_counter function returns a function where counter is added by 1. make_counter function is then assigned to variable count. Each time count is invoked, the anonymous function contained in (and returned from) the make_counter function still has access to the parent function's scope, in this case, variable counter. So after count gets called, counter increase by 1. In console, it logs:
0
1
2
Ruby Closure
Ruby also supports closure but in a slightly different way. Ruby's method doesn't behave the same as function in Javascript as method is not an object in Ruby. So we can't use
def a_method
def b_method
end
return b_method
end
to simulate what
function(){
return function(){}
}
does in Javascript.
In Ruby, closures are supported through procs and lambdas. Since this post is emphasis on closure, I'm not going to talk about subtle difference between procs and lambdas. I use lambdas to explain how closure is done in Ruby.
Reuse the same example as making a counter.
def make_counter()
counter = 0
return lambda {
counter+=1;
return counter
}
end
count = make_counter()
puts count.call
puts count.call
puts count.call
make_counter function creates a closure, using the lambda construct, and then returns it. We then assign our closure to a variable count. count.call executes the lambda construct defined in make_counter function. It can still access counter variable defined outside lambda.
The result:
$ ruby counter.rb
1
2
3
The closure concept remains the same, an inner function when executed after outer function returns can still access outer function's defined variables. The main difference between Javascript and Ruby is Javascript can make function returns function to implement closure, while in Ruby it's lambdas(or procs) returns from a method.
Python Closure
Python shares the same function concept as Javascript. A function can be returned within another function. Same make_counter function in Python:
def make_counter():
x=0
def counter():
x+=1
return x
return counter
count = make_counter()
print count()
print count()
print count()
If you execute above code, you will get an error message:
UnboundLocalError: local variable 'x' referenced before assignment
The gotcha in Python is that even though it supports closure, inner function can only have read access to the outer function's variables, not write. So if you directly try to write to outer function defined variable, error pops up. The walk around in Python 2 is to have:
def make_counter():
x=[0]
def counter():
x[0]+=1
return x[0]
return counter
count = make_counter()
print count()
print count()
print count()
Here you are not directly change variable x reference. You are only modifying its stored value. That value keeps added by 1. You will then get the desired outputs:
$ python counter.py
1
2
3
In Python 3, you can use nonlocal keyword.
def make_counter():
x=0
def counter():
nonlocal x
x+=1
return x
return counter
count = make_counter()
print count()
print count()
print count()
And you'll get the same result.
Conclusion
closure is essentially storing some state which will persist as long as this closure lives on. It's part of functional programming concept. Javascript, Ruby and Python all support closure, but in a slightly different way.