My first post showing some GScript on this blog compared sorting in Java with sorting in GScript. Today I spent a lot of time going over the psychosis of Comparators in java, so I thought I’d share what I relearned for about the third time since I first started working with generics.
(As a side note, as I’ve said elsewhere, any non-trivial application of generics requires me to stop and put on my generics hat, and, ten minutes after I’ve understood and solved the task at hand, *poof*, it’s gone.)
Comparators, as everyone knows, are java’s way of defining orderings for collections of objects. In Java 1.5 and greater, they are paramterized, quite reasonably, on T: if you want to sort a List<T>, you pass in a Comparator<T>. Sort of. We’ll get to that. First, let’s look at a simple (!!!) example of sorting in java:
List>Employees> someEmployees = getEmployees();
Collections.sort( someEmployees, new Comparator>Employee>(){
public int compare( Employee e1, Employee e2 ) {
return e1.getSalary().compareTo( e2.getSalary() );
}
});
return someEmployees
What that code does is sort a collection of employees by Salary. You are forgiven if you can’t tease that out of that mess.
Anyway, take a look at the signature of Collections#sort(list, c):
static <T> void sort(List<T> list, Comparator<? super T> c)
What the hell does that mean? Basically, it means you
can pass in any comparator that is parameterized on T or a
supertype of T. This is because the comparator is
going to be invoked with objects of type T, so only
comparators that take that type or *higher* in the
inheritance chain will work. In particular, Comparator<?
extends T> will *not* work, because the comparator might
be expecting a subtype of T. This is a case of
contra-variance, which is fairly rare in type-systems. We
are used to the opposite, variance, where a subtype of T is
acceptable in place of T. Some programming languages,
notably Scala, allow you to annotate type variables with
the particular variance they allow, usually using a ‘+’ or
‘-’ sign. Interestingly, since java lacks any way to
specify variance, internally Sun engineers have to cast the
Comparators to the generic type to do comparisons. I’m not
lying. Check out the implementation. The implementation of
TreeSet is even better, with the developer putting in
little /*-*/ comments in the places to
indicate variance more correctly when he has to cast to the
generic type.
So here we have a lot of syntax flying around to make sure sorting lists is absolutely, positively, 100% type safe. And, despite all that, the sun engineers still have to downcast to make the damned thing work. All to prevent what has to be one of the rarest programming errors I can imagine: accidentally sorting a List with a bad Comparator. I’ve never seen that happen. I’ve never heard of that happening. I’ve never even heard it mentioned that it might happen. And yet all that complexity has been thrown at this problem in java.
As I wrote in the previous post, the GScript to do the above is:
return Employees.sortBy( \ e -> e.Salary )
Now, in my mind, that’s throwing complexity at the right
part of the problem. It takes a common (the most common?)
operation on a List, sorting it by some attribute of the
elements in that list, and boils it down to its essence
using closures. Admittedly, closures take a bit to get your
head around, but once you do you end up having to know
less, not more, about the complexities of sorting.
This is in stark contrast with the generics in
Collections.sort().
In GScript, no generics hat is necessary. Just sort the damned list and get on with it.
This is an overriding theme in GScript, formally spelled out by Scott at the start of our Bedrock release: “GScript features are about making developer’s lives easier.”