06 July 2011

Why Closures?

In a language with reasonably rich partial function application, it seems to me that there's very little need for implicit closures. In pseudocode:

greeter = λname.
    message = 'Hello, ' + name + '!'
    return λε. print(message)

greeter("world")()

Could easily be rewritten explicitly as something along the lines of:

greeter = λname.
    message = 'Hello, ' + name + '!'
    return λm. print(m) fix message

greeter("world")()

Where `fix` is an operator accepting a lambda and some parameters and returning a new lambda with the given parameters fixed, that is, λx. f(x) fix y is identical to λε. f(y). With the appropriate consideration given to reference semantics, this would have exactly the same effect; indeed, C++0x lambdas can be written with explicit closures exclusively:

return [&message]() -> void { std::cout << message; }

Supporting implicit capture not only complicates implementation; as with exceptions and globals, implicit capture reduces locality and makes analysis more difficult. Limiting functions to explicit capture requires support for lambdas with multiple parameters, because you cannot write:

sub = λx. λy. (x - y)

Instead you must write:

sub = λx, y. (x - y)

Which lends itself to partial application quite nicely:

neg = sub(0)
ident = sub(?, 0)

It would seem, then, that the primary reason for supporting implicit capture is historical, namely the roots of functional programming in the lambda calculus. And really, since implicit capture is the basis of scope, the fact that a functional language can easily survive without it calls into question whether the concept of scope is necessary or good. Functional programming is all about making state explicit, after all. Why not hold a function accountable not only for values it uses, but also for operations? I intend to play with this idea in the future.