Type Inference — more than just a pretty face

If you have looked at Scala at all, you undoubtedly know that it infers types. Whereas in Java you might say something like this:

HashMap<String,ArrayList<Person>> groups =
    new HashMap<String,ArrayList<Person>>();

you would never have to repeat the type like that in Scala. Yes, that saves you work. And yes, more importantly, it makes your code easier to read (we read much more code than we write). And yes, it means less work if we change the type later, because the type appears in fewer places. In fact, often the type of a variable is never written out explicitly at all:

val groups = Map( "presidents" -> List( Person("Obama"), Person("Bush") ) )

But those are all about convenience. Tonight it dawned on me that there is something else, something beyond convenience, that you get by not having to mention the type.  It has to do with anonymous classes.

Let’s say you are using Java and you want to hide the nasty details involved in communicating with a device we’ll call a framis. You have to get a connection to the framis from a pool, return the connection when you’re done, handle the exception you get if the session expires by reestablishing the connection, do the real work in another thread which you wait for via a Future so that you can time out the operation, and so on. You don’t want that kind of code repeated dozens or hundreds of times, so you create a FramisJob class that handles all that. To use FramisJob, you create an anonymous subclass of it with a go() method that does what you want to do, and invoke a val() method on the new object to kick things off, like so:

Float temperature = new FramisJob<Float>() {
    Float go() throws Exception { return ...; }
}.val();

FramisJob’s val() method performs whatever heroics are required to talk robustly to a framis, invoking go() in another thread when all is ready, and returns whatever result is returned by go() after doing any needed cleanup. FramisJob itself looks something like this:

abstract public class FramisJob<V> {
    V val() {
        V value = null;
        ... // do setup
        try {
            value = go();
        } catch ...
        }
        ... // do teardown
        return value;
    }
    abstract V go() throws Exception;
}

So off you go, happily using FramisJob without repeating all that setup and teardown boilerplate. Your code is cleaner and more robust.

But then you start running into cases where you need to return two or three values. Because Java doesn’t support closures, go() can’t assign to variables outside the FramisJob directly; in Java an inner class can only use outside variables if they are final. Darn. And in most languages you could just return a tuple, but Java doesn’t offer tuples either. Foiled again!

Then you come up with an idea — define extra variables inside the class itself and pull them out afterwards, like so:

FramisJob job = new FramisJob<Float>() {
    int humidity;
    Float go() throws Exception {
        humidity = ...;
        return ...;
    }
};
Float temperature = job.val();
int humidity = job.humidity;

Unfortunately the compiler doesn’t like your idea. The job variable is of type FramisJob, and a FramisJob does not have a humidity member. Only your extension of FramisJob knows about humidity. Strike three. In order to do this you’ll have to define some additional helper class to hold the returned data.

Finally we come to the epiphany about type inference. Java knows the type of that anonymous class you created, but you do not; there is no name for it — it’s anonymous. Because you have no way of parroting back to Java the type of the thing you just created, a type Java already knows, Java won’t let you access the object’s members. If Java had type inference, you would not give a type for job, it would get the right type automatically, and you could access its members. It is the very lack of type inference that prevents you from accessing the variables and methods you define in anonymous classes.

So, type inference is not just another pretty face — it has semantic consequences! And I will be so happy when I no longer have to fight with Java’s lack of closures and tuples and type inference and tail call optimization and …. After all, if we were using Scala, we would just be writing (and reading) this:

val (temperature,humidity) = FramisJob( () => ( ... , ... ) )

[other Scala posts]

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: