BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Lambda Update

Lambda Update

This item in japanese

Now that the dust has settled on the future of OpenJDK and Plan B confirms the feature slip of Lambdas into JDK 8 (or later), what is the future of Lambdas themselves?

The initial lambda proposal was fairly wide reaching, in that it attempted to bring functional types to the Java language. However, some fundamental issues in the intersection of the Java type system and the newly proposed functional types resulted in these functional types (or structural typing) being dropped in favour of existing class-based (or nominal typing) representations. Partly, this is due to the fact that if X is a subtype of Object, then array[X] is a subtype of array[Object]; combined with functions that throw exceptions, it is possible to break the Java type system.

Since then, the Lambda proposal has centred around providing anonymous instances of classes; in effect, replacing the common boiler-plate code of anonymous inner classes. The most recent proposal confirms that each lambda will be an instance of a type referred to as a SAM type; an interface, or abstract class, with exactly one abstract method. Many of the existing inner class approaches implicitly use SAMs in any case (Runnable, Comparable etc.) so this has wide relevance to the existing use cases to which Lambdas are expected to be applied.

The most recent draft has addressed one of the more controversial syntax elements of the proposal; the use of a separate keyword yield to indicate a return-from-lambda. (The initial rationale for avoiding using return was to permit a lambda to trigger a return from its enclosing method; however, the ability to wrap existing code and replace with an in-line modification “transparent lambdas” was seen by some as desirable; however, since Java does not represent blocks and control constructs as in Smalltalk, it was less likely that full transparency would have been achieved anyway.)

Another modification is the syntax change for lambda arguments. In the previous draft, one would write #(int a) { a*a } to write a 'square' lambda. In the current draft, it has been replaced with #{a->a*a} which is more compact, if not slightly different syntax than Java methods. If the compiler can infer the type (for example, it is being assigned to an SAM type, or it is unambiguously calling a function with known argument types) then it need not be specified. In cases where it cannot (such as the above; a could be a int, float etc.) it is necessary to type the argument with #{int a -> a*a}.

The addition of type inference, although primitive in comparison to other functional languages, will cut down on a significant proportion of characters in the requirements to set up a lambda expression. Proponents of dynamically typed languages will no doubt find these features in a statically compiled language useful; though it should be noted that other compiled languages (such as Scala) have a more powerful inference mechanism. Whether type inference in Java expands to other parts of the system remains to be seen; project coin already has support for inferring generic types during assignment.

More controversially, the meaning of this inside a lambda has been flipped since the previous draft. Previously, this referred to the lambda instance, and Outer.this referred to its enclosing class, much like the way inner classes are resolved. Now, this refers to the enclosing class and there is no formal way to refer to the lambda itself. The draft addresses this by allowing variable definitions to become self-referential during assignment; in other words, allowing something like int a = a + 1; which is invalid in today's Java code. The example in the draft is:

Timer timer = ...
final TimerTask t = #{ 
    if (somethingHappened())
        // cancel the timer task that this lambda represents
        t.cancel();
    else
        System.out.println("foo");
});
timer.schedule(t);

The problem is that the type of #{..} is effectively an anonymous MethodHandle, which has no concept of type. Instead, when this is assigned to a type in a local variable, it is promoted to an effective subtype of the SAM. So whilst #{..} is the method handle, a wrapper (of type SAM) is created to hold it; so you can invoke itself recursively and other methods on the SAM type. The argument is that the method handle doesn't implicitly know its own type (as this is only known when the compiler infers the arguments where it is used) and so can't be used to implement recursion or access any fields or methods from the superclass instance.

The draft seems quite mature at this stage, and the implementation has made good progress. Not enough for it to be included in JDK 7, but none the less likely to cause a significant impact when it does arrive in JDK 8. The existing behaviour of extension methods (aka defender methods) as well as exception handling remains the same; in fact, the only open question appears to be whether MethodHandles are Serializable; and if not, what is the behaviour if they are assigned to a Serializable object.

What do you think of this latest progress?

Rate this Article

Adoption
Style

BT