Checked exceptions, declared as part of subroutine signatures, are appealing. They make the abstraction interface more readable, and ensure error handling consistency.
However, they introduce coupling into the system, can be seen to break encapsualtion, and can cause a ripple effect of changes during maintenance.
This article weighs the pros and cons.
Posted to Software-Eng by Ehud Lamm on 3/11/03; 3:53:32 AM
The second point is probably designed to try to prevent breaking the encapsulation, by limiting the propagation of changes.
I would actually be interested to hear opinions about language design. Are all-checked exceptions a false good idea, that reveals too much of the implementation? Are unckecked exceptions a significant hole in compile-time safety in practice?
In Nice, all exceptions are currently unchecked. Note that with higher-order functions, it is not trivial to check exceptions precisely. For instance, imagine:
<T,U> U appy(T->U f, T v, U default) =
try { return f(v); } catch (SomeException e) { return default; }
the apply(f,v) throws every exception that f throws, *except* SomeException. So you would need at least type variables representing thrown exception, and set substraction.
I am also concerened about the other problems mentioned (esp. impact on maintenance costs).
The more I think about it, it seems to me that we are trying to do too much. Routine signatures should specify, in the name of abstraction and quality of interfaces, which expcetions are explicitly raised by the routine.
The transitive closure sounds like a good idea until you consider the implications.
The point is that interfaces are (amond other things) about documentation. Just as DbC is important even when you rid your code of contract violations.
You lose these nice properties when you have large C or Java programs that are all running inside one process and have to recover from partial failures manually. I think that checked exceptions are (at least in part) a response to this problem: you have no adequate "default" error handling, so you have to make sure you explicitly handle your errors correctly, and checked exceptions make sure you don't ignore error cases. If you did leave a Java exception uncaught, you could get into trouble - your thread may terminate without the rest of the system knowing, your files and sockets may stay open (hopefully to be finalize()d by the garbage collector some day, but not necessarily.) This has been a real problem for me in my Java programming.
In C without checked exceptions, I think there are a lot more ignored error cases than in Java in general (at least in my programs :-)).
Now, I'm sure this will surprise you all, but I rather like Erlang's approach. It gives you back the operating-system-like invariants on processes, and the processes can be small enough to realistically represent a single task, e.g. copying a file or serving an SMTP request. When an Erlang process opens a file, the file is linked with the process, so if the process terminates the file will get an "EXIT" signal and close automatically.Also, because processes are also linked with each other you don't need one part of the system to explicitly notify another that it has crashed and needs to be fixed, because the linked processes will find out automatically (and either trap the EXIT signal and recover, or crash aswell.)
So, it seems to me that Erlang avoids the annoyance of checked exceptions, while still being less prone to not recovering properly from errors.
Although, Java's <tt>finally</tt> and Lispy <tt>with-open-file</tt> constructs do seem to make a lot of simple cases easier. But, it doesn't seem as general for longer-lived things to require that the cleanup code is on the stack.
(Perhaps I'm taking too narrow a view of what you want to do in response to exceptions..)
From a language desing point of view, I prefer the Java approach over C++/Ada. Java has both checked and unchecked exceptions, giving the the programmer the freedom to choose what's appropriate in each case.
What I meant was that you indeed check that the exceptions are explicitly raised (I am talking about programmer defined exceptions). I agree this complicates dummy methods a bit, but it is similar to Ada dummy functions that require a "return" statement.