“Closures are one of the uniquely wonderful things about Lisp. They open a door to programming techniques that would be inconceivable in other languages.” — Paul Graham, ANSI Common Lisp p 109
“Object-oriented programming is exciting if you have a statically-typed language without lexical closures or macros. To some degree, it offers a way around these limitations.” — Paul Graham
“Lexical closures provide a way to get the effect of subroutines when the ui is just a series of web pages.” — Paul Graham
A closure is simply a function within a specific context. When you use Common Lisp’s lambda to create a new function on the fly, you can pass it any number of variables that are currently in scope. The resulting function will “remember” those values regardless of what else happens in the program.
I must admit that I don’t immediately see the full extent of the power this concept. I do know that in object oriented programming as I’ve done it in the past causes me to define numerous small classes each with their own private fields, constructors, and Get/Set properties. It can be a pain to constantly write these out each time you want to add a new “type” your the application. With Lisp, you can skip all of this busy-work and focus on the part of the program that actually does something. With closures, state can be hard-wired into dynamically generated functions– because in many situations there’s really no need to specify an entire class.
Working with C# 1.0, I had to use an entire file to make a strongly typed collection class. With “generics” introduced in C# 2.0 I could create them with a single line of code. Obviously an improvement! (Though, being infected by smug Lisper memes I now question the “essential-ness” of compulsively strongly typing everything in sight….) But the point is that anything that eliminates entire files from your project improves the expressiveness, conciseness, and maintainability of your code. Closures are similar to generics in that respect, but they operate at a more fundamental level.
Closures are a concept that exists in a nebulous no-man’s land somewhere between a function and a class. Programmers bred in today’s OOP-obsessed environment will have to unlearn a great many habits and assumptions in order to use them. But with “a hash table full of closures” to define our system… what exactly have we got? We end up in a situation where, in a sense, the properties of our system do much of what classes do in other programming languages. We can forget about an entire level of implementation… and the basic building blocks of our thought processes gain an order of magnitude in their scope and depth. In short, we can think bigger ideas… and implement them faster. Ah well. As I began writing this, I was disappointed at how most material on Common Lisp glosses over the concept of closures when guys like Paul Graham speak of them in such glowing terms. I was going to write an small article explaining exactly why I could only muster up a low-key “so what?” after reading about them. This post represents an attempt to parse a few cryptic remarks based on only my own meager programming experiences. Take them with a grain of salt! I’ll see if these “egg-head” ideas actually hold up in the face of getting something non-trivial done with Common Lisp. I’ll report back here on my experiences as I try to apply these ideas….
After coding a bit, I can see that closures are just one of many elegant tools provided by Common Lisp that allows you to avoid writing tedious and unnecessary code:
Common Lisp gives you the ability to store functions in variables and pass them around to other functions. The mapcar function gives you the ability to apply those functions to each item in a list. Recursion on lists allows you to avoid declaring and managing iteration variables.
Common Lisp gives you access to the evaluator. Functions are themselves stored as lists… and the evaluator can be applied to parts of those lists selectively. This gives you the ability to quickly write dynamically defined functions that are initializable according to the current environment. Closures expand that capability by allowing you to persist parts of that environment along with your function.
Because lisp instructions are stored as evaluated lists that are expressible in plain text, you get a form of “object serialisation” for free. Each data type has its own textual representation… and when lisp objects are written out to text files, they are generally human readable and editable. I imagine this technique existed well before the XML craze and so forth, but even today this is an attractive feature. Unfortunately, closures don’t play nicely with this, so you can only save more mundane objects like lists and hash-tables, but still there’s a lot of power here that comes built in to the language.
Even a mediocre developer like myself can accomplish things that are relatively difficult in other languages. Yes, you will have to write a few “library functions” here and there that you get for free on other platforms– but this is much easier that writing an evaluator and/or a dynamic function generator. Closures are one of many features of Common Lisp that enable clean and concise dynamic designs without requiring a lot of housekeeping.