A closure is a pair consisting of a code pointer and an environment pointer. The environment pointer contains all of the free variables of a given function. For example:
fun f(a, b) =
let fun g(c, d) = a + b + c + d
in g end
val g = f(1, 2)
val result = g(3, 4) (*should be 10*)
The function g
contains two free variables: a
and b
. If you are not familiar with the term free variable, it is a variable that is not defined within the scope of a function. In this context, to close over something, means to remove any occurrences of a free variable from a function. The above example provides good motivation for closures. When the function f
returns, we need to be able to remember what the values of a
and b
are for later. The way this is compiled, is to treat function g
as a code pointer and a record containing all the free variables, such as:
fun g(c, d, env) = env.a + env.b + c + d
fun f(a, b, env) = (g, {a = a, b = b})
val (g, gEnv) = f(1, 2)
val result = g(3, 4, gEnv)
When we apply the function g
, we supply the environment that was returned when calling function f
. Note that now function g
no longer has any occurrences of a variable that is not defined in its scope. We typically call a term that doesn't have any free variables as closed. If you are still unclear, Matt Might has an excellent in depth explanation of closure conversion at http://matt.might.net/articles/closure-conversion/
Same example in Javascript
Before closure conversion
function f(a, b){
function g(c, d) {
return a + b + c + d
}
return g
}
var g = f(1, 2)
var result = g(3, 4)
After closure conversion:
function g(c, d, env) {
return env.a + env.b + c + d
}
function f(a, b, env) {
return [g, {"a": a, "b": b}]
}
var [g, gEnv] = f(1, 2)
var result = g(3, 4, gEnv)