Friday, 25 January 2008

Closures - Comparing closure type inference

In this blog I'm going to compare the three principle 'closure' proposals based on how they infer types. This follows my previous comparison blogs.

Here are the links to the proposals if you want to read more:

  • BGGA - full closures for Java
  • CICE - simplified inner classes (with the related ARM proposal)
  • FCM - first class methods (with the related JCA proposal)

Type inference

Type inference is where the compiler works out the type of a given expression without the programmer needing to explicitly state the type. For example:

 String str = "Hello";

In the above, the type of str is known to be a String because it is stated by the developer. However, there is no actual need for the developer to manually state the type of 'String'. Instead, the compiler (not the Java 1.6 compiler) could work it out itself by infering the type from the literal "Hello" - hence 'type inference'.

Closures

Lets consider how the closures proposals allow some limited type inference in Java. Consider this single method interface:

 public interface PairMatcher<T,U> {
   boolean matches(T arg1, U arg2);
 }

The purpose of the interface is to allow developers to write a callback that takes two arguments and returns true if they 'match' some criteria that is important to the developer. Here is an example of such a framework method, that searches through two lists and returns the matched entry:

 public T find(List<T> list1, List<U> list2, PairMatcher<T,U> matcher) {
   for (T t : list1) {
     for (U u : list2) {
       if (matcher.matches(t, u)) {
         return t;
       }
     }
   }
   return null;
 }

And here is how calling this code looks today using an inner class:

 List<String> stringList = ...
 List<Integer> integerList = ...
 String str = find(stringList, integerList, new PairMatcher<String,Integer>() {
   public boolean matches(String str, Integer val) {
     return (str != null && str.length() > 0 && val != null && val > 10);
   }
 });

BGGA

With BGGA, the code can be shortened to use use a closure:

 List<String> stringList = ...
 List<Integer> integerList = ...
 String str = find(stringList, integerList, {String str, Integer val =>
   (str != null && str.length() > 0 && val != null && val > 10)
 });

Obviously the code is shorter. But it is important to understand what has occurred. The type PairMatcher, and its generic arguments, have been inferred. This happened as the BGGA compiler identified which method was being called. In order to call the method, a 'closure conversion' occurred, which changed the closure to a PairMatcher with the correct generics.

So, not only is the type inferred, but the generic arguments are as well.

FCM

FCM can be used, like BGGA, to shorten the original:

 List<String> stringList = ...
 List<Integer> integerList = ...
 String str = find(stringList, integerList, #(String str, Integer val) {
   return (str != null && str.length() > 0 && val != null && val > 10);
 });

Exactly the same conversion and type inference is occurring as in BGGA. Thus, for this scenario of type inference, FCM and BGGA have the same power, just different syntax.

CICE

CICE also provides a means to shorten the original:

 List<String> stringList = ...
 List<Integer> integerList = ...
 String str = find(stringList, integerList, PairMatcher<String,Integer>(String str, Integer val) {
   return (str != null && str.length() > 0 && val != null && val > 10);
 });

I hope that this example makes it clear that there is no type inference here as there was in FCM and BGGA. Instead, the developer still has to type the interface name and, more significantly, the generic arguments.

While just typing the interface name might be regarded as a documentation benefit by some, I would suggest that it is very hard to justify the retyping of the generic arguments. Clearly, for this scenario of type inference, CICE has less power than FCM and BGGA.

Comparison

Both BGGA and FCM support the same style of type inference for the single method interface and it's generic arguments. CICE does not, and with generics that results in quite a verbose result for the developer to type.

Summary

There are two basic approaches here - infer or be explicit. My view is that this is an area where inference is really needed. With CICE, I find it hard to see how this is really much of a gain over an inner class.

Opinions welcome as always :-)

10 comments:

  1. Given the widespread discussion about eliminating a lot of repetition of generics arguments, I think its somewhat disingenuous towards CICE to presume that a CICE proposal wouldn't go hand in hand with a proposal that shortens or eliminates generics types. In particular, because CICE is new syntax anyway, and there's always the long-winded alternative, you can say that for CICE syntax, if you don't add generics parameters of any sort to the class name, they are inferred, instead of the current default of treating it as a raw type.

    With that taken care of, the only extra content that the CICE proposal saddles you with is the PairMatcher name, which really is useful.

    NB: I filed a bug report for joda time at the sourceforge page but I'm not sure you check it.

    ReplyDelete
  2. Why are we suddenly discussing adding academic language features to Java?

    When was the last time you wanted to use closures to solve a real-world problem?

    I write distributed trading systems that deal with millions of dollars daily and I've never felt the need for closures or inference. On the other hand the academics I talk to are infatuated with closures, loose typing, and monads, and have never written anything mission-critical in their life.

    ReplyDelete
  3. Stephen Colebourne26 January 2008 10:53

    -Reinier: Yes, CICE might build in support for type inference of generics over time, but I can only discuss what the current proposal states. Also, according to Neal, this inference problem also affects exception transparancy in CICE.

    -Ahmed: Closures are not an academic language feature - the vast majority of languages have them. They are not new (they've been around for decades), and they are well proven.

    Every day I work with Java I find a situation that would be better solved using closures. Typically, these are looping around lists and maps to find things, although there are many other use cases. A key reason why they are appropriate now is the Fork-Join framework from Doug Lea, which addresses the coming concurrent revolution in processors.

    For the record, I am very much not the academic. Yet, I know that some form of closures (FCM) would be very valuable for Java developers, and as the frameworks adapted, even you would be using them every day. Finally, neither loose typing nor monads would be especially appropriate for Java.

    ReplyDelete
  4. Hi Stephen,
    i see your point but i tend to agree with Reiner, CICE version provides self documentation by revealing that operation is a PairMatcher contract.

    ReplyDelete
  5. CICE is clearer to me. For a java developper, it's very close to the inner class approach.
    Personnaly I think type inference is a scripting language feature. And I'm happy that java is strongly typed.

    ReplyDelete
  6. I'm working on an application that is almost as mission-critical as they come (people have died when we made bad design decisions). The nature of the code is thousands of concurrent high-latency network calls, and we've implemented what amount to closures using generics; we're very much looking forward to closures being included in Java.

    I had a piece of code I was working on where fully 80% of the codespace was generics definitions; It was defining a chain of closures from one type to another to another to define my business rules. We chose to give up the strong compile-time typing to make the code at all maintainable; with good LHS type inference, we would get both the compile-time strong typing, and maintain the readability of the code.

    From what we've been experiencing, type inference is a predicate for closures.

    ReplyDelete
  7. BGGA is the only one that looks remotely good.

    FCA's syntax is silly
    CICE is no better than the AIC.

    Clinton

    ReplyDelete
  8. Pity you didn't mention C3S:

    http://www.artima.com/weblogs/viewpost.jsp?thread=182412

    It has more inference than the other proposals. Your example would be:

    final stringList = ...
    final integerList = ...
    final str = find stringList, integerList, method(str, val) {
    __str != null && str.length() > 0 && val != null && val > 10
    };

    ReplyDelete
  9. Stephen: About monads; they're not so much a language feature as a general way of thinking. One could argue that checked exceptions are an example of a monad. I'd suggest learning about them before commenting on them, though I do need to take my own advice here!

    I should note that BGGA deliberately avoids being concerned with type inference so that it doesn't get rejected because of any controversy over type inference.

    ReplyDelete
  10. Ahmed,

    There are people outside of academia who want closures. I also happen to write distributed trading/pricing systems (just on slightly bigger scale) and I'm also not using closures - because they are not there. Unfortunately, to create the systems properly, there is a lot of quite verbose code patterns we have to follow to have everything working in right way. Good closures could cut the noise in source code by half probably.

    Yes, it would be a kind of subdialect of java and nobody from market could understand the code without some basic explanation. Unfortunately, it is also a case today - fact that you can understand the operations done, doesn't mean you understand the reason why they are done. In both cases, somebody with knowledge have to explain it to you. Difference is that after explanation, you will have to either 'parse' it in your mind every single time to match the pattern or see the shortcut syntax with correct keyword.

    Not having custom control structures causes copy/paste on the code pattern level (not on implementation level of course). Plus a lot of undeeded verbosity, which still won't allow 'Average Joe' to understand inherently complicated system.

    ReplyDelete