Skip to content

Alternatives to Closures (java)

Gafter and Gosling themselves have written a proposal to add closures to java7.

If it happends, I’m switching to something else. That proposal makes Java a lot like Python. Except, of course, Python is a lot better at being pythonesque than java ever will be. duh.

Here’s an example straight from the closure proposal:
int(int) plus2 = (int x) {return x+2;};

In order to make this discussion a bit more lively, I’ll be focussing a lot on an actual live example from the JDK where Ruby and Python use closures – sorting a list. In python, sorting a list of integers in order of absolute value would work like this:
a = [-2, 1, -10, 3]
def compareFunction(x, y):
    return abs(x)-abs(y)
a.sort(compareFunction)

In the current version of java, the same functionality would be implemented as follows:
List a = Arrays.asList(new Integer[] {-2, 1, -10, 3});
Collections.sort(a, new Comparator() {
    public int compare(Integer x, Integer y) {
        return Math.abs(x)-Math.abs(y);
    });

If the closure proposal is adopted, this code fragment would probably look like this:
List a = Arrays.asList(new Integer[] {-2, 1, -10, 3});
Collections.sort(a, (Integer x, Integer y) {return Math.abs(a)-Math.abs(b);});

First, an analysis of the (dis)advantages of each of these 3 fragments:
Python v. current java
The python code is shorter. Walking proof that creating initialized lists should be simpler, but that’s not related to closures. The relevant bit, the closure, uses a combination of a named function (which does unfortunately contribute a variable name, which is annoying when it isn’t needed. You could use a lambda here but that only works for very simple one-liners, not always an option) and the general property in python that functions can themselves be used as objects. It’s a lot shorter vs. the java example.

What’s missing is typing information. There is no python definition file ANYWHERE that explicitly explains that the parameter to a list’s sort function must be a function with 2 parameters that returns an integer. There’s not even any explicit mention that the parameter must be a function at all. a.sort(“hello, world!”) does ‘compile’ – it generates a run time error. This is normal pythonic behaviour, and it’s very un-javalike. Java sacrifices short code for large scale maintainability: While in a small project you’ll just remember how everything works, in java the combination of explicit typing everywhere and the existence of tools like javadoc allow Eclipse to auto-complete your attempt to call the sort method, offer you to generate the exact comparator boilerplate (so that you know parameters and return type that you’re supposed to supply) and can show a popup of javadocs.

That discussion applies generally to the differences between java and python/ruby/javascript. It’s relevant here because the closure proposal works well… in P/R/JS, NOT in java.

Java vs. Proposal
The proposal code is as short as the python code except basic types are added. However, some type information has been lost between old and new: In the current java example, the parameter is a java.util.Comparator. If I’m confused about particulars, I can look there for relevant javadocs, and if I write a method that returns a Comparator, I don’t need to put anything of substance in the javadoc for this method assuming the methodname itself makes sense (ie: getAbsoluteIntegerComparator() or some such). In the new example, the ‘return type’ of that method would become an int(Integer, Integer) which might as well be something for adding two arguments together. Or for calculating the difference. There are literally infinitely many operations that map 2 integers onto 1 integer. (Preferably, actually, an enum with 3 choices, but, eh, that’s for another time). There’s no one specific type and there’s no javadocs to refer to.

That almost seems analogous to the choice of Exception to throw: A lot of programmers new to java, myself included for over a year, just picked a nice Exception from the java runtime library that sort of fit the problem. Now, though, I always make my own exceptions. It’s a bit more work (IDE helps) but it’s helped me avoid tons of bugs, and it makes code way more readable. It also allows me to pile generic conditions of a certain exception into the javadoc of the exception itself, instead of repeating myself over and over, re-mentioning it in every relevant method that throws it.

In other words, recycling exceptions is a bad idea. If you agree with that, I’m tempted to say that logically you should also strongly dislike this closure idea.

Alternatives

So, while the closure java is pretty bad, it still has one big thing going: It’s much smaller and more convenient. On the user side it’s less code, and on the library author’s side, it’s way less code, you don’t even need the separate interface.

Let’s see how close we can get with java5-inspired syntaxis tricks. Through a number of proposed, low-impact changes, I’m going to make it possible for the library user to sort this list as follows:
List a = new ArrayList(-2, 1, -10, 3);
a.sort(new Comparator(Integer x, Integer y) {return Math.abs(x) - Math.abs(y);});

Analysing the code above, this looks quite decent. It’s very short yet it loses none of the expressiveness and typing of the current original. It can also be reduced to plain old java1.4 code if you must, unlike the closures. There are 3 different improvements here.

1: interface model methods

Currently, ‘a.sort()’, where a is a List, doesn’t compile. It’s an error; the List interface has no sort method. Even if a would be an ‘ArrayList’ type, there’s no sort. Sort currently resides in ‘Collections’, but starting from java 1.6, interfaces are allowed to have static methods. It would make sense for the sort() method to live in the List interface, as only lists can be sorted.

That would lead to List.sort(a) which is still ugly. The next step is a compiler-optimization step only; it compiles down to the exact same code as the old way, it’s just easier to write, and easier to read. If a method cannot be found in the structure itself (not in the class, or the superclass, or its superclass, etcetera), then look for all relevant interfaces with the same signature except with an extra parameter as first parameter: the object type itself.

In other words, a.sort(Comparator) compiles to List.sort(a, comparator), provided the following caveats are true: ‘a’ is of a type that has no (non-static) ‘sort’ method anywhere in its hierarchy. There is exactly one static ‘sort’ in the hierarchy of a’s type, which has as first parameter something compatible with ‘a’ itself. This avoids problems with multiple inheritance (if two different interfaces both have candidate methods, then the compiler refuses to compile, and you have to fix the code by hand. It won’t happen often, but where it does happen, compiler error is much preferred over vague and hard to remember rules of precedence. That’s asking for bugs).

2: function interfaces

Any interface that has the @Function annotation (and has only 1 method not copied over from Object.java) can be implemented using the special function syntax:

new interfacename(parameters for method){method body};

Any generic types can be specified as usual (between interface name and the parameters, in lesser than/greater than brackets), but they will be inferred where this is possible (usually possible if the parameters themselves cover all generic types. Usually true, and true in this example). The parameter list and method body simply become the implementation of the sole method inside the interface. This eliminates the useless repetition in most function interfaces: The ‘Comparator’ has a single method named ‘compare’. Gee whiz. The method could be called ‘flooburgle’ at this point; the name of the interface itself covers the meaning perfectly already.

3: Collection literals

Creating ready initialized versions for the collection interface (a map, list, or set initialized with values in the code itself) is a real pain. Try to properly make a constant map. It’s ridiculous. Here’s the answer:
private static final Map HTML_CODES;
static {
  Map t = new HashMap();
  t.put(404, "Not Found");
  t.put(403, "Forbidden");
  t.put(100, "Continue");
  HTML_CODES = Collections.unmodifiableMap(t);
}

Fixing THAT, either with actual literals or with library cruches like an Object… constructor for e.g. ArrayList – that’s useful. Cuts down on massive boilerplate – boilerplate you absolutely need to do it ‘right’, like the above example.

This closure thing takes attention away from more important fixes, and takes java entirely in the wrong direction: The direction of becoming a poor man’s python. Being the dynamic programming language’s retarded cousin is, hopefully, not where java is going to go. It needs to take its current stance as king of the static languages and expand on this – show that it beats pure dynamic programming whenever the projects grow beyond the ability of one man to completely comprehend.

{ 5 } Comments

  1. Tom | 2006/08/27 at 16:19 | Permalink

    Ahhh, +1 on the closure suggestion. I was just reading Neal Gafter’s explanation on closure I got very very very afraid. Yikes!

    I think it could be more generic; interfaces which define only one methode may use an “anonymous method notation”, using your notation. So an:
    interface x( public void y(int z){…}}

    may be used as:
    new x(int z){…};

    Oh, and personally I already implemented a MapUtil.addAll(Map, String) where the String uses a “a => b, c => d” notation for simple constant maps.

  2. Tom | 2006/08/27 at 16:24 | Permalink

    Ok, made a typo on the interface definition, but the idea is clear I believe.

  3. rzwitserloot | 2006/08/28 at 04:39 | Permalink

    Thanks for the comments!

    True, in theory the @Function annotation is not actually required; simpy declaring only one* method is enough to peg the interface as a model for a function object.

    However, I like the aspect of explicitly documenting the intent. It also makes it much easier to identify design brainfarts for library designers. If you try to add a second method signature to an interface with the @Function annotation. your IDE could immediatly flag an error. It’s a Good Thing that IDE designers are aware they just screwed over a lot of users when they migrate to the newer version.

    Perhaps a compromise: You don’t HAVE to, it’s entirely optional. Supplying it simply guards against accidentally adding another signature later. Eclipse and NetBeans can fight amongst themselves as far as warnings are concerned. Similar things happend with @Override, and while personally I’d prefer a mandatory @Override, I can configure Eclipse to give me exactly that (error if I override a method without adding the @Override flag).

    *) One unique method; that is, one method not already available in Object.java itself. For example, Comparator, a clear functor if ever there was one, defines two methods. The practical one (compare), but object’s own equals is also stated, apparently only for clarifying how Comparators should reason about equality amongst Comparator objects (NOT amongst the things they compare), in the corresponding javadoc. It’s beyond me why this is, I’d kick it out in an update of the spec, but there it is.


    As far as your addAll goes: neat. It’s a much nicer solution compared to the ginormous heap of boilerplate I use. Your method has the downsize of not enforcing anything of note, though; string isn’t checked, types aren’t confirmed. Can only use primitives and Strings. Personally I’d like to see actual map, set, and list literals. Eh, one can only hope Sun gets its mind back on this.

  4. Erling | 2006/09/13 at 11:17 | Permalink

    Just a little note on map literals: with the new closure syntax, you can do this:

    private static final Map<Integer,String> resultCodes =
    MapBuilder.with(404, “Not Present”)
    (100, “Continue”)
    (200, “OK”).build();

    It will type-DWIM on the parameters to the with() method, so it is fully type safe.

  5. rzwitserloot | 2006/09/13 at 11:31 | Permalink

    Wait a minute. Brilliant! You don’t even need closures. This is perfectly viable:

    MapBuilder.with(404, “Not Present”).with(100, “Continue”).with(200, “OK”).mutable();

    (with a tandom immutable()). The first ‘with’ returns some sort of builder object, and all subsequent calls to that object’s with method all ‘return this;’.

    I used the same trick to create an SQL layer that translates queries like this:

    new Select().from(USERS).fetch(USERS_NAME).where(GreaterOrEqual.make(USERS_BALANCE, 1000)).orderAscending(USERS_BALANCE).limit(100);

    and that works fine as well.

{ 1 } Trackback

  1. [...] Wherein I reveal a neat little tool to create list/set/map literals for java 1.5, inspired by Erling’s comment (4th comment) from my previous article. I assert that arrays are pretty much ‘deprecated’ in java: the collections API (lists, sets, maps) are just more flexible in every way; there is very very little ground where it makes sense to use an array because they can do stuff better. Maybe for speed reasons for computer graphics programming and other such esoteric areas. [...]