Checked Exceptions Discourage Closures

My friends and I have long speculated that the reason the Java collections library does not use internal iteration and instead requires the caller to be responsible for the mechanics of iteration is that closures in Java are tedious and crufty. However new depth was added to this notion while I was taking a shower one day and I realized that checked exceptions in Java make closures a head scratcher.

Let's look at this in some detail.

What are Closures?

Roughly speaking, a "Closure" is a function which "encloses" some local scope (in other words, it has access to some local variables) and is passed as a parmater to another function.

When might we want to use closure-like structures in our programs? Very often we have a lot of set-up and tear-down which is always the same, but what we do in the middle changes. For example, let's say we regularly have to call some set-up code which obtains a database connection and begins a transaction. Also, we have to regularly, call the tear-down code which issues the commit or rollback and then returns the database connection. Of course, we could repeat something like this all over the place:

   Connection con = databse.beginTransaction();
   try {
       /* ... code here ... */
      database.commit(con);
   } catch (Exception e) {
      database.rollback(con);
      throw e;
   } finally {
      database.done(con);
   }  

But we really don't want to, since the amount of code that goes in to the block will tend grow. Every change will need to be tracked to each place. Just imagine when we add nesting to this so that we only do a commit on the outermost level, and not in between. That will be a lot of code to add to each place. Duplication is the sneaky enemy of all coders, of course.

We'd much rather just be able to pass the the "code here" section into the database object, and have it deal with the whole messy setup and teardown for us. Perhaps a bit like this closure inspired approach:

interface DbAction {
    Object execute(Connection con);
}

class ActionExcutor {
    Connection setUp() {
        // maybe get connection from ThreadLocal or create new one
        // do whatever additional setup is needed
    }

    void tearDown() {
       /// whole lotta stuff
    }

    Object asTransaction(DbAction action) {
        Connection con = setUp();
        try {
            Object result = action.execute(con);
            // commit
            return result;
        } catch (Exception e) {
            // rollback
            // log, rethrow, whatver
        } finally {
            tearDown();
        }
    }
}

Then when we want to do some database stuff, all the boilerplate we need is something like this:

    database.asTransaction(new DbAction() {         
        public Object execute(Connecion con) {
            /* ... code here ... */
        }
    });

Okay, so closure-like structures could be less ugly, but this is Java and that's just the syntactic salt we have to deal with when useing a closure-like pattern. We might see slightly better closure support in Java 7. However, in addition to the syntactic heft, what makes today's situtaion really sticky is when we think about Checked Exceptions.

Checked Exceptions in the context of Closures

If we were to use checked exceptions inside the DbAction, then the "execute()" and "asTransaction()" methods would need to declare the exceptions we might throw.

Since this will be re-used through-out the code, that means that every block of code that throws an exception needs to get added to the interface.

What's bad about this is that in one particular path, we know that only one type of excption is thrown, however because some other bit of code somewhere else in the application throws a different checked exception, we are now forced to in both places to believe that we need to be prepared to handle an exception that will never happen. In this scenario, checked exceptions are not communicating more information, they're communicationg wrong information. This is not a theoretical problem. This happens naturally as codebases grow. In fact, I've watch this start to happen on two codebases -- in both, every operation that touched the database would have had to declare that it threw FooException, BarException, BoomException, etc. creating a strong desire to simply declare "throws Exception" even though on any given use it may not have thrown any at all. Rather than add "throws Excpetion" to every method in the codebase, extending RuntimeException is the usual solution.

In the end, the idea that checked exceptions could be more informative to the callers is lost ....

Fundamentally, I suspect this is why Java is the only* language to have checked exceptions.

I also wonder if the initial excitement about the power of checked exceptions is a strong contributing reason that the Java core libraries are nearly devoid of closures or closure-like interfaces. The notable exceptions being "Runnable" and "Comparable" which only allow runtime exceptions. I now think that it was not simply the syntactic weight of closures in Java, but also the love of checked exceptions which created a mental block against design-for-closures.

Imagine if the Java.util.Collection used internal iteration. This would seem like a more OO design, yes? Today collections foist the job of iterating over the collection on the callers -- which is not only duplication -- but a rather backwards inversion of responsibility. We can imagine that the Collection could have a "forEach" method which took a closure as an argument .... This is how many OO language collections libraries work. However this immediately creates a dilemma: the collections library would have to choose between declaring no checked exceptions or forcing all collections users to deal with plain old Exception every time they wanted to iterate over a list.

Other Objections

We are not the first people to discover problems created by using checked exceptions, and others have written about it. Many people suggesting that all exceptions should be unchecked. If we google for "checked exceptions" we get these sorts of links:

etc., etc.

I can imagine that closures could work with exceptions in a way similar to the way generics and typing work in Java 5 -- but that's not the case today as far as I know -- and I know of no plans of that nature.

Conclusion

In the end I find this 2004 IBM "Java theory and practice" quote in-escapable:

    Recently, several well-regarded experts, including Bruce Eckel
    and Rod Johnson, have publicly stated that while they initially
    agreed completely with the orthodox position on checked
    exceptions, they've concluded that exclusive use of checked
    exceptions is not as good an idea as it appeared at first, and
    that checked exceptions have become a significant source of
    problems for many large projects.

Like many others, I have nearly completely abandoned the use of checked exceptions. For me, it was dealing with closure-like framework code, as discussed above, that was the biggest nail in the checked exception coffin.

Further thoughts

When I first shared my thoughts on this with Brett Neumeier, he suggested that once we have a fully built-it-from source OpenJDK, it might be fun to try an experiment. Perhaps we could rename and re-organize a couple of classes. We could change "Exception" to "CheckedException extends Exception" and we could change "RuntimeException" to "Exception extends Throwable" and finally, to avoid a vast array of compile errors, we could create a new deprecated class "RuntimeException extends Exception" ... it might be worth a shot.

-- Eric Herman
eric@mysql.com


Footnote: One might argue that technically Java is not the only language with checked exceptions. Firstly, there Java forks and variants, however, I think we can still loosely think of these as "Java". Also, OCaml has some form of optional checking, and CLU and Modula-3 have something similar, but as I understand it, they are not the strict compile time checking that forces the code cruft the way Java does. And, of course there may be more languages in development that are not obvious from a web search on the topic. If you are interested in seeing what other languages have similarities to Java's checked execptions, you might start with Kevlin Henney's comments (which Bruce Eckel kindly includes for us at the bottom of his article on checked exceptions) and the wikipedia entry on checked exceptions .... I think you'll come to the conclusion that the claim stands; Java is unique.


Wish us luck!

Valid XHTML 1.1