Ask HN: How do you explain closures?
In the past year or two, I've had to explain closures--what they are and why they are interesting--several times. Usually, at the end of my explanation, the person doesn't get it, and they go back to doing whatever they are familiar with.
So, I ask HN to put yourselves in this scenario and tell me what your answer would be:
Joe is a talented but inexperienced developer. He's got a year or two of experience, maybe as much as 5, but he programs for a job, not for fun. Also, for whatever reason he's never encountered closures--most likely he's only worked in languages that don't really support them.
You want to explain to him (a) what they are and (b) why he would want to use them. You have about 10 seconds to get his interest before he starts thinking about his WoW raid tonight, and about 5 minutes for the whole explanation.
What do you say?
71 comments
[ 3.1 ms ] story [ 149 ms ] threadChastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.
On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.
http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/m...
I would take the route of first order functions -> anonymous functions -> closures, but I think it is really a lost cause for the typical 9-5 programmers.
But those are three completely different concepts. A closure need not be anonymous or even first-order.
I don't think this guy played WoW, though. :)
See http://www.r6rs.org/formal-comments/comment-6.txt, for example.
I explained this to someone recently in the context of event handlers in ActionScript/JavaScript, starting with the idea that when you do:
you're passing a function around just like it was any other value.Then I moved to an artificial example of a 'magic function-making machine':
with a couple of examples of storing and using the functions that giveMeAnAdder returns.Then I pointed out that, normally, local variables in a function disappear when the function returns, so how does my adder 'remember' what baseNumber is? And the answer is that the anonymous function is actually a bundle of a function body and the local context, and we call this a closure.
Why do you want me to appreciate a ferrari if all you do with it is go pick up some groceries...
I usually find it's better to introduce a language idea to someone and let them try to find out where they might put it on their own - they may surprise you. If you're doing code reviews, you could use that as an opportunity to point out how something like a closure (or any other construct) might have improved a situation, or, at least, how it may have changed it.
Two examples where I tried to induce that kind of brain lock in Perl programmers are http://www.perlmonks.org/index.pl?node_id=34786 and http://www.perlmonks.org/index.pl?node_id=50132.
Your claim that Joe is a talented programmer isn't very persuasive. Edit: maybe I should say why. I have known many programmers of the 9-5 type who do a passable job, but are not interested in learning anything. It is as if they had a limited supply of energy for learning and used it up when they were getting started. This is a common type of working programmer and you made it sound like Joe might be one.
For example, in Javascript:
In javascript, defines and executes kamikaze function. It's a function which dies as soon as its job is completed.We can use the kamikaze to hide secret_private from all but the functions that we want to have access to it.
Since fn is defined in the kamikaze function, fn can access secret_private.
Since fn is assigned to a global variable, it can be accessed from outside the kamikaze.
When the kamikaze dies, it takes all of its variables with it, except for the ones that are referred to outside of the kamikaze.
temporary disappears because it is referred to only inside the kamikaze.
But, since secret_private is referred to in fn, and fn is a global, secret_private does not disappear, but now only fn can refer to it.
So sceret_private is essentially a private variable of fn.
With Closure:
Without closure:It would have made more sense if I had some other piece of information to inject in, like if you had a database reference that needed to be passed in to the Delete() method.
Then:
Vs. EDIT: fixed(And of course you mean "d.DoDelete")
What they are: functions with state.
Why they're useful: callbacks where a whole separate method or class would be overkill.
Motivating examples:
* Delegating work to a background thread. Captured variables are an easy way to pass along necessary state.
* A generic benchmarking routine. Benchmarking requires taking a measurement before the code, running the code to be measured possibly multiple times, and taking another measurement after the code. If you can turn the code to be measured into a callback, then the benchmarking code can be written once and reused elsewhere. But having to put all your benchmarks into their own methods, much less worry about state and possibly creating classes and initializing data in constructors, is pretty tedious. Closures avoid this tediousness.
(Yes, I'm conflating several concepts here, anonymous functions and lambda closures, for colloquial and didactic reasons. Nitpicking doesn't buy you much in terms of practicality when learning here.)
In Python, you would probably just make this a class. (But sometimes it's nice to have something equivalent to a class that's automatically built for you, and closures are that something.)
So what are the mechanics? Follow me (I'll use Arc):
Lesson 1) A function can return a function:
Lesson 2) A created function can hang on to information which you pass in: Lesson 3) The information a function hangs on to can be mutable local state. And that is a "closure". The name comes from the fact that the scope of the created function "closes over" the local variable.Helpful or noisy?
In particular, I focus on showing an example that is noticeably more complicated without closures, but also a real case that could conceivably come up.
Surprisingly, most of the people I talk to assume that the "fancy" parts of closures (such as variable value capture and upvalues) are the way such a construct should work and require no further explanation to use them. People who are surprised by capture (and assume, for instance, that the variable will take on the most recent value assigned to it by outside code instead of "remembering the value when the closure was made") get a more lengthy computer science-esque explanation. But I generally find that explanation to be unhelpful if a person can't see why closures are useful; "An anonymous function with some teeth" is often how people I talk to come to closures first, so it's how I let them think about it until the model is no longer helpful.
A class in PHP, for example, can have any number of class variables and for this demonstration consider these variables to be "private" (not accessible outside of the class or its own instances). One can, however, write methods or functions in the class that can provide an interface to operate on or access these private class variables.
That is, essentially, what a lexical closure is but without the overhead of an object system (remember, an object system can be created using closures!). They are a remarkably useful pattern of encapsulating certain kinds of logic.
Continuations, are another fun topic related to lexical closures too...
Say that closures lower the syntactic overhead of accessing the same flexibility he normally turns to objects for.
So if he uses closures he will effectively use objects in more situations simply because it becomes easier and this will increase the flexibility of his code.
http://darwinweb.net/articles/41-ruby_blocks_as_closures
It really focuses on just one aspect of closures that Ruby's syntax makes especially convenient, which is that they enable you to write generic functions that don't need to know a single detail about what kind of data they are being applied to.
Easy.