I haven’t updated this in a while since I’ve been working on my thesis and papers. In that sense, I’m going to do what the TV networks do: run some repeats! This is an adaptation of something I wrote about closures (in CS) some years ago.
Every time I tell myself “I’m going to finally understand closures today!” and I make the mistake of going and reading about them, my brain seizes up like an overheating car engine.
I think it’s mainly to do with the way that the writers of these articles write the definition of “closure”: it is way too obtuse. After reading these things repeatedly, I think–I think–the simplified definition is “A function that is created at runtime that, in addition to defining its own local variables, contains references to the variables of its creator’s local scope. This created function can continue to use and alter these variables once its creator goes away.”
I think that’s right. It’s certainly better than this: “A function that can refer to and alter the values of bindings established by binding forms that textually include the function definition.” That’s from here. I keep reading that over and over and it’s only very slowly making sense to me.
But why do closures break my brain (and possibly yours)?
First off, the name. A closure, in mathematics, is a concept/object in set theory. This is the first thing I think of when I hear the term, not this magical function-object-thing.
Secondly, I think it’s because of the kinds of systems I’m used to working on: the very environment that you need in place to make a closure work is utterly alien to me. Specifically, the ability to create functions from thin air; i.e., functions as a first-class object. More modern languages have this feature, but when you’ve spent as long as I have not using functional languages, it’s sort of mind bending.
That is, to fully understand closures, you must (mostly) eschew the traditional stack-oriented method of tracking local variables.
Consider the following example implementation:
Instead of allocating space for local variables on the stack when a function is called, like what you’d normally do in stack-based environments, allocate them on the heap, with some sort of reference count or other garbage collecting scheme.
When a closure is created in this environment, it’ll hold a reference to these heap variables, which gives that unique trait of persistence once the creator’s stack frame is popped off. You’ll also get the ability to create multiple closures with multiple calls to this creator function, each with their own copies of this environment. If no closures are created, these variables will just be garbage-collected once the potential creator function returns.
It’s that last part that’s the “alien” part: stack variables living on once the creator has returned. That’s just such a weird concept to some people, and people who are used to using closures don’t quite see that, I think.
Later, I decided to do something rather scary. I’ve hidden it after the jump.