Dr. Gafter comes to SJSU

I teach a graduate programming languages class at San Jose State University. In order to inject some topics of current interest, I had a lab about closures and the competing closure proposals for Java 7. I got an email from Neal Gafter: “Hey, it's really cool to see your reference to BGGA in a SJSU lab assignment!” I asked if he could give a talk at the department seminar, to which he graciously agreed. We had a packed room today. I am very excited that my students had a chance to witness a bit of history in the making. Here are my impressions of the talk.

The End of History - Not.

I have taught CS252 for many years. I cover the lambda calculus, closures, continuations, metaprogramming, and other advanced programming language topics. For several years, my graduate students viewed the material as having little relevance to their lives. Clearly, real programming languages had evolved from C to C++ to Java, and the end of history had been reached. Every other language was either some poorly designed pile of crud or an ivory tower obsession.

I tried what I could, talking about continuations for web applications and Paul Graham's article on Lisp in a startup. My students, however, are by and large a pragmatic bunch. They know that web applications aren't written in Lisp, and googling for Paul Graham led them to this funny but unflattering article.

This year, things were finally going my way. The buzz behind Ruby and Rails is hard to ignore, and it seems pretty clear that Java will get closures in some form or other. Learning about metaprogramming and closures doesn't seem a waste of time any more.

How Not to Write a Proposal

Alex Miller has a nice blog on Java 7 proposals, including the competing closure proposals. My students found the CICE proposal pretty straightforward. It starts out with a simple promise:

For example, here is the Java 5 code to start a thread whose run method invokes a method named foo:

    new Thread(new Runnable() {
        public void run() {
            foo();
        }
    }).start();

If we adopt this proposal, the following code would be equivalent:

 new Thread(Runnable(){ foo(); }).start();

The first impression is that this is easy and convenient.

In contrast, the BGGA proposal starts out with:

Modern programming languages provide a mixture of primitives for composing programs. Most notably Scheme, Smaltalk, Ruby, and Scala have direct language support for parameterized delayed-execution blocks of code, variously called lambda, anonymous functions, or closures. These provide a natural way to express some kinds of abstractions that are currently quite awkward to express in Java. For programming in the small, anonymous functions allow one to abstract an algorithm over a piece of code; that is, they allow one to more easily extract the common parts of two almost-identical pieces of code. For programming in the large, anonymous functions support APIs that express an algorithm abstracted over some computational aspect of the algorithm. For example, they enable writing methods that act as library-defined control constructs.

I translated this into plain English in my lecture:

We are smarter than you are.

The proposal then goes on to discuss closure literals, function types, closure conversions, exception type parameters, definite assignment, the type Unreachable and control invocation syntax. That section finally contains a compelling example:

with(FileReader in : makeReader()) with(FileWriter out : makeWriter()) {
    // code using in and out
}

No more try/finally/in.close. Hooray!

When we discovered that BGGA gives us this and CICE doesn't, we were getting much more excited about it.

The Presentation

Neal told me before the talk that he has given this presentation fifteen times already—someone ought to make a movie about him tirelessly criss-crossing the planet and spreading the message :-) Actually, you can see a video of the presentation here.

He started out with this quote:

"In another thirty years people will laugh at anyone who tries to invent a language without closures, just as they'll laugh now at anyone who tries to invent a language without recursion." - Mark Jason Dominus

I am not sure whether my students understood this—they had probably never seen a language without recursion. Sure, I tell them in the undergraduate programming languages class about Fortran, but they view it as a freakish thing, like a two-headed cow, not as something that was taken seriously in its time. But Neal went on to say that one now says the same thing about garbage collection, and my students do remember the dark ages of C++ and having to call delete (but not too often).

Neal systematically defined closures, discussed why anonymous instance creation expressions fell short, defined the requirements for a true solution, went over syntax, and finally presented the examples. The talk is much easier to follow than the spec, and everyone enjoyed it tremendously.

Still, if I had given the talk, I would have given the examples first. Since this is my blog, I get to do it my way, so here goes.

Closing Locks, Files, Database Connections

Don't you hate writing this code?

Connection conn = dataSource.getConnection();
try {
   . . .
} finally {
   try {
      conn.close();   
   } catch (SQLException ex) {}
}

On a bad day, I even worry about a SQLException that triggers the finally clause, only to have a second exception occur in conn.close() that masks the first one.

With BGGA closures, you write

with (Connection conn : dataSource.getConnection()) {
   . . .
}

Iterating over Maps

I can't ever remember whether it is

for (Map<String, Integer>.Entry entry : map.entrySet()) {
   String key = entry.getKey();
   Integer value = entry.getValue();
   . . .
}

or

for (Map.Entry<String, Integer> entry : map.entrySet())

I either look it up in Core Java or, if I am lazy, I sheepishly fall back on

for (String key : map.keySet())

and feel bad about paying for the additional get.

With BGGA, I can have

for eachEntry(String key, Integer value : map) {
   . . .
}

I want that.

For that matter, I want to iterate over a two-dimensional array as

for eachIndex(int i, int j : matrix)

without abominations such as a[i].length. I also want to have both index and value when iterating over a list. With BGGA, I can make control structures for those purposes.

Callbacks

All the closure proposals handle this use case, but here goes, for the sake of completeness:

myButton.addActionListener({ ActionEvent event => model.myButtonAction(); });

It seems a shame that one has to do something with the useless ActionEvent. Ideally, I'd like to be able to ignore the parameter and write

myButton.addActionListener({ => model.myButtonAction(); });

But maybe it's asking too much.

Logging Exceptions

Neal didn't talk about this, but I gather from the spec that this must be possible.

I hate writing reflection code like this:

try {
   m.invoke(arg1, arg2);
}
catch (IllegalAccessException ex) {
   logger.log(Level.SEVERE, "", ex); 
} catch (IllegalArgumentException ex) {
   logger.log(Level.SEVERE, "", ex); 
} catch (InvocationTargetException ex) {
   logger.log(Level.SEVERE, "", ex); 
} 

If I understand the proposal correctly, I can instead write

<throws IllegalAccessException|IllegalArgumentException
      |InvocationTargetException>.tryAndLog(logger) {
   m.invoke(arg1, arg2);
}

It's still not pretty, but I can't fault BGGA for a cumbersome API with a method that throws three unrelated exceptions.

Process and Politics

Neal talked a bit about the process of getting a language extension accepted for the Java language. The JSR process seems rather company-centric and doesn't seem to deal all that well with the possibility that two people from the same company (in this case, Neal Gafter and Josh Bloch, both of Google) have differing views. Still, he hinted that something will happen in the near future about getting a JSR on closures started.

Overall, my impression was that Neal's approach is right. You want to be able to take a block of code, wrap it up and hand it to some other code and just have it work. Anonymous inner classes won't do it, no matter how much syntactic sugar you sprinkle over them. When evaluating the competing proposals, I suggest that you look at the use cases first before you get flummoxed by the syntax (which may well change) and the arcane considerations of definite assignment.