Designing language extensions is not just black art, we are trying to follow a certain number of principles. One of them could be called 'the precautionary principle", or as Guy Steele put it "planning for growth". In short, do not add features if they may hamper future developments. When in doubt, stay on the safe side. When facing a choice, if no argument stands out clearly in favor of one side or the other, devise restrictions in order to avoid making a choice rather than take the risk of having to carry the consequences of a wrong choice all over the language life time.
One specific example comes to mind : the design of "closures" for Java, which is currently a heavily debated topic. If you haven't been brainwashed by programming teachers, "closures" are a very natural thing: wrap some piece of code so that you can carry it away exactly like you would do for data. "Closures" are also the basic building block of lambda-calculus, widely recognized as the theoretical basis of computation, and available for decades in functional programming languages.
With "closures", you can define methods or functions that take as parameters not only data, such as integers or strings, but also things to do, in other words pieces of code that will be executed when you evaluate the parameter. If you're familiar to C, this is similar to a pointer to a function (often used as callbacks), with the difference that you do not have to provide a name, and that the type system makes sure that you're not breaking things. Outside the Java world, "closures" are commonplace in functional languages such as Caml and scripting languages such as JavaScript. "closures" have also recently been added to C#. As the story goes, "closures" where even on the feature list of the first version of the Java language, and were dropped because of time-to-market considerations.
The most widely publicized proposal for "closures for Java" today is named BGGA by its authors' initials, and I strongly feel that this proposal is breaking the precautionary principle mentioned above. Let us try to remove the quotes around the word "closure". Theoreticians and functional programming languages talk about "functions" and "functional languages". A closure in this context is an instance of a function that remembers part of the context where it has been defined, for instance the value of some local variables. In some sense, this object is "closed", hence the name: it has its own copy of the context it needs, and can be carried away as a black box.
The problem is when you try to add functions to an imperative language such as Java. Variables in functional languages have one and only one value, while variables in imperative languages have values that can change in time; they refer to a memory location rather than to a mathematical value ("final" variables in Java are somewhat close to functional variables). Should a closure remember values or memory locations ? Said otherwise, if some value is changed within the closure, should the change be propagated to the original context ?
The BGGA answer is yes. I can see two reasons for this choice. One is about the list of features that a magazine would use for a comparative review : because most scripting languages today do that, if Java would appear as lacking a feature. The other reason is that closures in the BGGA proposal are in effect suggested as an answer for two really different things : macros (designing your own control structures) and functions (carrying away pieces of code). The meaning of what is a context, where should "return" return to, and so on, are totally different for macros and functions. Not surprisingly, macros and functions are compiled in very different ways.
My answer is no. This is not about some subjective notion of language aesthetics, this is about the precautionary principle. Closures are not only about writing more concise code. Intuition and theory tell us for instance that closures are the basic building blocks of parallel programming (google for "pi-calculus"). You want to be able to express distributed computation as closures that are dispatched among different processors or computers. But this is just plain impossible if closures are able to modify arbitrary locations in their originating context.
To put it shortly, the BGGA proposal for closures will go in the way of future language extension for parallel programming, which is the next "big thing" (most PCs sold today have 2 or 4 cores, and PCs with 100's of cores are not far away). The trade-off is between making a nice marketing announcement today and growing a language that will be able to handle multicore processing tomorrow.
One reason for this misunderstanding is the ambiguous meaning of the word "closure", hence the quotes. As you have guessed, I strongly recommend to drop this term and use instead "function" and "macro".
See also my first comment on this topic on Neil Gafter's blog and a few references about closures in general.
Thursday, December 6, 2007
The precautionary principle
Subscribe to:
Posts (Atom)