bad.robot

good robots do what they're told

Lambdas vs. Closures

When writing Java in a functional style, things tend to get very verbose. We often create a bunch of anonymous implementation fragments and pass them around akin to a function in functional languages. These fragments often get called closures or lambdas, but what’s the difference between the two terms?

Lets take the WaitFor class from tempus-fugit as an example where we pass an anonymous instance as a parameter to the waitOrTimeout method. We define a function here that will be called at some later point by waitOrTimeout. We can think of this as lazy invocation. Java isn’t a functional language but we’ve simulated at least one characteristic of a functional language using WaitFor and an anonymous function. We’ve created a higher order function but not necessarily a pure function (1).

For example,

...
server.start();
WaitFor.waitOrTimeout(new Condition() {
   @Override
   public boolean isSatisfied() {
      return server.isRunning();
   }
}, timeout(seconds(5)));

The anonymous class implementing Condition is evaluated by the method waitOrTimeout (which will call the isSatisfied) method.

The recent shift to this functional style has lead to eager anticipation of JDK7 and the promise of closures. More accurately however, it’s the inclusion of lambdas that we’re waiting for, not closures. Closures have in fact been available in Java since 1.1, so what’s the difference?

Lambs to the Slaughter

So, we want to be able to define anonymous functions on the fly, the result of the function is purely dependent on it’s arguments and this is called a lambda. Those functions that depend on external values (not just it’s arguments) are when closures come into it. The act of binding those external values to the anonymous function is referred to as closure. After closure, when all variables have been captured and bound to the function, the term is closed.

For example, the code snippet above will return a new Condition instance on each invocation. Because it will bind the variable server to the anonymous function, it will return a closure. To put it another way, we’ll extract the anonymous part to a method to explicitly create a new instance, such

private static Condition isRunning(final Server server) {
   return new Condition() {
      @Override
      public boolean isSatisfied() {
         return server.isRunning();
      }
   };
}

This should make it more obvious that the variable outside the scope of the anonymous Condition is required (the server variable), each call to the isRunning method will return a closure over the argument, the instance of which captures the value of server. Java implements the closure by passing a reference to the outer scoped (lets say Foo.class) to the anonymous class (Foo$1.class). The access$000 call accesses the appropriate private field in the outer class directly in the bytecode

class Foo$1 implements Condition {

    final Foo this$0;

    Foo$1() {
        this$0 = Foo.this;
        super();
    }

    public boolean isSatisfied() {
        return Foo.access$000(Foo.this).isRunning();
    }
}

So, if, we have update the example again, this time removing the out of scope variable, we’re left with something like this;

private static Condition isRunning() {
   return new Condition() {
      @Override
      public boolean isSatisfied() {
         return true; // optimistic!
      };
   }
}

Then no out of scope variables are required, the term doesn’t need to be closed. The anonymous function that is left is effectively a lambda.

Summary

A lambda will generally be more efficient that a closure as it only needs to evaluated once. As a closure closes over something not in it’s environment, it has to be evaluated every time it’s called.

What JDK7 will (finger’s crossed) bring is more explicit, concise way of expressing the same ideas. It will support lambdas as a language feature although I can’t quite figure out what the example would look like in those terms. See the straw man proposal and see if you can figure it out!

Over to you...