Thursday, May 15, 2008

RE: Would Type Inference help Java

This is a response to an interesting blog post by Ola Bini of JRuby (and other) fame.

The question, raised by his friend, is: "Would Type Inference help Java"

Maybe we have a different view of how type inference would work, because I think it would be a welcome addition. More in a bit.

First I want to point out one stupid thing about Java Generics and constructors:

Map<String,Employee> empMap = new HashMap<String,Employee>();

This is quite possibly the most useless waste of typing that you can do in Java. The generic parameters to the right of the assignment are absolutely, 100% useless. They are immediately erased and do NOTHING.

There hasn't been a greater demonstration of superfluous typing since Mavis Beacon Typing Tutor.

When there's a constructor to the right of the assignment, it's just as meaningful to simply type:

Map<String,Employee> empMap = new HashMap();

There's no difference at all, save one. The compiler will toss a warning out, and most IDEs will figure this out in advance and hightlight this as an unsafe assignment (which it is absolutely not). I gather it's because most IDEs and the compiler don't/won't/can't distinguish between:

Map<String,Employee> empMap = new HashMap();

//...VS...

Map<String,Employee> empMap = someUntypedMapLookup.getEmployeeMap();
//where getEmploeyeMap() returns an untyped Map.

In the latter case we might quickly think it's fair for the compiler to highlight and show the warning. But here's the really stupid thing though: The compiler/IDE will warn you that it's an unsafe operation, and casting it here removes the warning. BUT!!! (you know what's coming).... No error is ever thrown here if you are casting an incompatible Map at this point. So why warn me? It's absolutely ridiculous.

Map getMapFullOfCatsAndDogs() {...}

Map<String,Employee> empMap = (Map<String,Employee>) getMapFullOfCatsAndDogs();

Works just fine until you actually attempt to use the map. And even then you could simply downcast it and use it inappropriately (which is actually appropriate to the contained type). The only time Java generics really provide meaningful infomation in Java is if both sides are typed:

Map<String,Pets> getMapFullOfCatsAndDogs() {...}
Map<String,Employee> empMap = (Map<String,Employee>) getMapFullOfCatsAndDogs();

...OR...

Map<String,Employee> empMap = new HashMap<String,Pets>();

That last case makes me want down a bottle of vicodin, because the only reason there's a problem at all is that generics exist. And there's actually no consequence or prolem here save for someone decided to try to do the right thing in Java by typing as much as possible (probably to satisfy the IDE warning), and failed dramatically. It's a self imposed problem!

Anyway, in both cases this does provide meaningful information, but is actually a compiler ERROR, not the same warning as before. I really think the earlier warning and any extra typing related to it is a waste of characters in source code. Currently in Java, there's nothing wrong with:

Map<String,Employee> empMap = new HashMap();

Unless Sun has eliminating type erasure among their future plans, in which case you might argue that you're future-proofing your code with the extra typing.

Now, onto the topic: Type Inference!

I really think type inference would help, and I'd be more than happy to reverse that typing to:

var empMap = new HashMap<String,Employee>();

There's no reason it needs to be any more complicated than that! None! The compiler should just copy the type on right side of the assignment, thus:

var empMap = new HashMap<String,Employee>();

//...would be equivalent to typing....

HashMap<String,Employee> empMap = new HashMap<String,Employee>();

Yeah, sure, we're no longer coding against the interface. But pretty soon Java and Java developers are going to have to stop all of the abstraction arm waving that basically is only useful for bragging at parties. (Do Java developers even have parties anymore? You know, where they actually talk about Java and not JavaFX?)

Type inference is only useful in local method variables, and thus there's need not be any risk of leaking implementation details into the class definition, which is where abstraction actually matters (and where Ruby is weak and Groovy's optional typing is a welcome feature). The var keyword would not be allowed on method member fields and definitely not method signatures (obvious!).

There are about 10 things I think could be added to the Java compiler without any consequences, only improvements, to the code quality of a typical Java program. I'll save the rest for some other post.

I wish I was as committed a blogger Ola!

Cheers,
Clinton