Lambda the Ultimate

inactiveTopic The Trouble with Checked Exceptions
started 8/18/2003; 4:56:07 PM - last post 8/29/2003; 2:07:26 PM
Chris Rathman - The Trouble with Checked Exceptions  blueArrow
8/18/2003; 4:56:07 PM (reads: 3519, responses: 49)
The Trouble with Checked Exceptions
This is the second in a series of interviews of Anders Hejlsberg at Artima as discussed in a previous LtU thread. Anders weighs in on the subject of Java's checked exceptions.

It is funny how people think that the important thing about exceptions is handling them. That is not the important thing about exceptions. In a well-written application there's a ratio of ten to one, in my opinion, of try finally to try catch. Or in C#, using statements, which are like try finally.

In the large, checked exceptions become such an irritation that people completely circumvent the feature. They either say, "throws Exception," everywhere; or—and I can't tell you how many times I've seen this—they say, "try, da da da da da, catch curly curly." They think, "Oh I'll come back and deal with these empty catch clauses later," and then of course they never do. In those situations, checked exceptions have actually degraded the quality of the system in the large.

I can't say that I agree with his assessment. First off, I don't think that recompilation is the same thing as altering code. Any client code that handles general exceptions or drops into a finally clause is already code ready. If the compiler regenerates the code and gives it a new version, then it's the compiler's fault for not recognizing the binary did not change with the new version.

Also, I believe that exceptions thrown are part of the interface, just as much as the return value of a function itself. If a function is given a total new class of exceptions, then it's likely that there is a reason to uniquely propagate those exceptions. If it's just a refinement, then the exceptions can be subclassed and handled polymorphically as if they were the same as the original specified exceptions.
Posted to OOP by Chris Rathman on 8/18/03; 4:59:28 PM

Chui Tey - Re: The Trouble with Checked Exceptions  blueArrow
8/18/2003; 6:23:12 PM (reads: 2284, responses: 0)
In reality there are too many exceptions that may occur. Having to explicitly write handlers or specify the exceptions often add very little to the readability of code.

Consider the following:

public string getCalculation()
{
    1. open temporary file for writing
    2. write stuff to temporary file 
    3. open temporary file for reading 
    4. return contents
}

Here are all the possible errors that can occur: a) No write access b) No read access c) Out of disk space d) Out of memory e) File does not exist for reading (even though we had created the file at step 2), since some other process might overwrite it.

This is trivial piece of pseudo-code. Now the programmer has to either declare all the exceptions up front or wrap them up as a single CalculationException. The former is too verbose and exposes too much internals, while the latter will generate insufficient traceback for the programmer to debug.

Pseudonym - Re: The Trouble with Checked Exceptions  blueArrow
8/18/2003; 6:50:29 PM (reads: 2312, responses: 1)

I agree with the above assessment, and will go one step further: Strongly statically checked exceptions fundamentally break abstraction, because any change in implementation will, in general, change the possible set of exceptions which may be thrown. This is especially true in OO programming, where an exception declaration on a base class/interface limits the possible implementations that derived classes can have.

Also, I believe that exceptions thrown are part of the interface, just as much as the return value of a function itself.

That's true. However, in C, it's also true that who is responsible for deallocating a piece of memory is part of the interface to a function, but I suspect that most people here don't consider that a "feature". :-)

Another example of something which is part of the interface to a function/method/whatever is locking semantics. In general, you may need to know what locks a function may try to obtain in order to avoid deadlock. However, we don't put these in the type signature either. (Yet. More research may be required here.)

Rich Dougherty - Re: The Trouble with Checked Exceptions  blueArrow
8/18/2003; 6:53:44 PM (reads: 2269, responses: 0)

I agree with Chris that exceptions form as much part of an interface as return values. If an interface I'm using is changed and throws a new exception then I want to know about it!

I think that a well designed interface should insulate calling code as much as possible from its underlying implementation. To avoid this an interface should be careful not to expose too much information about its implementation. A common mistake, I feel, is to do this in the exception declarations.

For example, consider a Dictionary interface. A Dictionary could have a number of implemenations: a hashtable, an array, a file, a database, etc. Each of these implementations could encounter different errors and so would throw different types of exceptions. It would be silly to declare all these exceptions in the Dictionary interface. Instead the Dictionary could declare its own exception type: DictionaryException.

Different implemenations of Dictionary would then catch their own exceptions and wrap them in a DictionaryException. This makes it much simpler for users of the Dictionary interface. It also removes the need to alter the interface when a new implementation (with different types of exceptions) is written.

I think to some extent this style of programming also addresses another point they raise in the article:

Bill Venners: What is the scalability issue with checked exceptions?

Anders Hejlsberg: The scalability issue is somewhat related to the versionability issue. In the small, checked exceptions are very enticing. With a little example, you can show that you've actually checked that you caught the FileNotFoundException, and isn't that great? Well, that's fine when you're just calling one API. The trouble begins when you start building big systems where you're talking to four or five different subsystems. Each subsystem throws four to ten exceptions. Now, each time you walk up the ladder of aggregation, you have this exponential hierarchy below you of exceptions you have to deal with. You end up having to declare 40 exceptions that you might throw. And once you aggregate that with another subsystem you've got 80 exceptions in your throws clause. It just balloons out of control.

In my opinion, higher layers shouldn't be exposed to implementation details of lower layers. Why would a webapplication want to know that a user couldn't be found because the database query failed because a network error occured because a hostname couldn't be resolved? It is usually enough to know that a user couldn't be found. (As Chui Tey mentions, it might be useful to know the original cause for debugging purposes. For this reason low level exceptions should be wrapped by higher level exceptions in a chain. This capability was introduced in Java 1.4: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html.)

Rich

nickmain - Re: The Trouble with Checked Exceptions  blueArrow
8/18/2003; 6:58:19 PM (reads: 2261, responses: 0)
Actually, all of those exceptions (except out-of-memory) would probably be extensions of IOException, and could be dealt with polymorphically.

Out-of-memory is OutOfMemoryError and is not a checked exception (or even an Exception) and does not need to be declared.

One common trick to avoid having to declare an exception all the way up a stack of methods is to wrap it in RuntimeException (the base for all non-checked exceptions) at some suitable point deep in the logic and then unwrap it just beneath the point where it needs to be exposed - probably at the public interface to the logic.

Margaret Green - Re: The Trouble with Checked Exceptions  blueArrow
8/18/2003; 8:33:58 PM (reads: 2244, responses: 0)
Chui Tey says "...wrap them up as a single CalculationException. The former is too verbose and exposes too much internals, while the latter will generate insufficient traceback for the programmer to debug. "

Proper attention to detail enables troubleshooting. Today cascaded exceptions enable both the cause and the context to be established. Prior to that, the astute developer captured both message and the stacktrace and added it to the message for the new application specific exception being thrown.

Published API's must not be changed, they should be deprecated and new API added. Exceptions are part of a method signature. Evolution of API should not break existing apps.

Versioning in the article is too fine grained. Versioning is at the release level of the component.

Pseudonym - Re: The Trouble with Checked Exceptions  blueArrow
8/18/2003; 9:10:57 PM (reads: 2243, responses: 0)

Rich wrote:

If an interface I'm using is changed and throws a new exception then I want to know about it!

Sometimes I do and sometimes I don't.

I expect exceptions to be thrown very rarely, and when they are, I usually won't want to deal with them at anything but the highest levels of my code excepting, of course, that the lower levels must be exception-safe. Moreover, I don't want to spend too much coding time dealing with uncommon cases. I certainly don't want to spend any design time worrying about what specific exceptions some high-level abstraction may throw, because that prevents me from thinking abstractly.

True, I can wrap exceptions, and language features like Java's anonymous classes can certainly help, but I still have to write (and think about) code to catch and rethrow around every public interface method.

Perhaps language support for automatically wrapping exceptions is really what I want here.

Curt Sampson - Re: The Trouble with Checked Exceptions  blueArrow
8/18/2003; 9:22:21 PM (reads: 2236, responses: 1)
I used to like the idea of checked exceptions, but these days I find that, at least as implemented in Java, they do more harm than good.

1. They don't provide useful assurance that you're dealing with all of the exceptions that could be thrown, because there still exist unchecked exceptions. It's small comfort to know that you would have caught any IOException that could have been generated when you're faced with a NullPointerException.

2. They force you either to break encapsulation or write more code. Why should my servlets care that the data repository can throw an SQLException? (Or that it can't, any more, once I switch to a different method of storing data?) They ought not, obviously, so I have to write code to wrap all of these checked exceptions in unchecked exceptions. This is not only more work, but makes debugging harder, because it's a bit difficult to get a good printout of just what was going on, without also getting a bunch of useless information that comes out only because you were forced to write this wrapper code.

One of the things I really liked about switching from C to Java was putting an end to the "must check the return value of every function." But now I'm in my own little hell of either checking what's thrown and wrapping it, or propagating SQLExceptions up into code that really should have no idea that it's accessing an SQL database (as opposed to any other kind of data store).

nraynaud - Re: The Trouble with Checked Exceptions  blueArrow
8/18/2003; 11:02:14 PM (reads: 2284, responses: 0)
Another example of something which is part of the interface to a function/method/whatever is locking semantics. In general, you may need to know what locks a function may try to obtain in order to avoid deadlock. However, we don't put these in the type signature either. (Yet. More research may be required here.)

Well, a few time ago, I wrote my first Haskell program (sorry, fully in french, for french newbies to read it more easily) and I realised that monads do more than give an evaluation order specification, they also enforce it in a type point-of-view ; you can't mix in-order and out-of-order code without sheating (SPJ, shame on you for giving the sheat-code in a excellent tutorial :-) ). More, the type of the monad gives the "aspect" on with the order must be respected (IO, concurency, etc.).

Maybe looking in this direction would be the solution ?

(and good luck to explain to the OO comunity that side-effects are bad :-) )

Daniel Yokomiso - Re: The Trouble with Checked Exceptions  blueArrow
8/19/2003; 5:03:14 AM (reads: 2130, responses: 0)
I think the primary problem in this issue is how Java (and most OOPL) deal with subtyping. Suppose we have two typing operators "<:" (subtype of) and ":>" (super type of, allowing type sums) and the following definitions (Java-like notation):


interface class Dictionary {
    static class ReadException <: Exception {}
    static class WriteException <: Exception {}
    String get(String key) throws ReadException;
    void put(String key, String value) throws WriteException;
}
class FileDictionary <: Dictionary {
    static class ReadException <: Dictionary.ReadException :> IOException {}
    static class WriteException <: Dictionary.ReadException :> IOException {}
    String get(String key) throws ReadException {...};
    void put(String key, String value) throws WriteException {...};
}

Voilá! The client can use the definitions of Dictionary.(Read|Write)Exception in their try catch block, or do a type-case for a more specific versions including IOException. Of course this requires some contortions in the type-system to either allow multiple-inheritance for exceptions, or declare exceptions as interfaces and some factory methods to create them (which would be better). AFAICS this kind of solution is amenable to type-inference, allowing a language with nicer syntax than Java to create a union type of all internal exceptions, something like (from a Neel Krishnaswami post, maybe from here or in the LL1 list):


compose f g : (a -> b throws F) -> (b -> c throws G) -> C throws F + G
compose f g = x -> g (f x)

A little bit of higher-order operations on types can make such discussions (checked vs. unchecked exceptions) much more like dynamic vs. static typing. Most of the time the participants are discussing a lousy statically verified system (either exceptions or types) against dynamic verified ones.

Jo Totland - Re: The Trouble with Checked Exceptions  blueArrow
8/19/2003; 6:55:29 AM (reads: 2092, responses: 0)
Personally, I'm much more interested in exception safety than in catching exceptions. And from that viewpoint, the only useful exception declaration is "nothrow". Limiting exception declarations to this has at least two big advantages:

First, it's simpler. I shouldn't have to mention this on ltu, but higher order functions and function objects really complicate exception declarations. Sure, we can check what can be thrown at any part in the code completely mechanically, but that doesn't mean that that information is something it makes sense to make into a "type" or "interface". It should simply be information we could ask the compiler or a code-analyzing tool to get for us. Having to do that task ourself is just silly.

Secondly, it's what we need. I have never had any need for knowing exactly which checked exceptions a piece of code throws. Sometimes I might be interested in knowing> which exceptions are considered part of a packages interface, but that is a documentation issue, and even if this set is limited to something sane, I could still get an unexpected NullPointerException at any time. However, being able to know that a given method will never throw an exception (including NullPointerException), is very important. For exception-safe purposes, this can then be used as part of an "atomic" operation in exception safe code. And it can also be used as an optimization aid by the compiler.

Kory Markevich - Re: The Trouble with Checked Exceptions  blueArrow
8/19/2003; 9:49:09 AM (reads: 2077, responses: 0)
1. They don't provide useful assurance that you're dealing with all of the exceptions that could be thrown, because there still exist unchecked exceptions. It's small comfort to know that you would have caught any IOException that could have been generated when you're faced with a NullPointerException.

The existence of unchecked exceptions is not a problem caused by checked exceptions, nor one that would be solved by removing checked exceptions from the language. NPE's in particular could have been dealt with in other ways, as the Nice language demonstrates.

2. They force you either to break encapsulation or write more code. Why should my servlets care that the data repository can throw an SQLException? (Or that it can't, any more, once I switch to a different method of storing data?)

As you mention in your next sentence, they shouldn't. If they need to know that operations on the data repository can fail, then they will have to deal with some sort of error reporting mechanism. Java provides you with extra tools (language features, stack dumps, etc.) if you make this mechanism an exception, but it doesn't force the issue.

Regardless of the choice though, the mechanism should only be in terms of the data repository's interface, not it's implementation. A SQLException may be a source of an error in the implementation of the data repository, but it should not be made part of the interface (which all error reporting mechanisms are a part of). Retrieval of the original error is useful for debugging, but is easily accomplished by chaining or other mechanisms and is really an orthogonal issue.

Really, this complaint could be made of any error reporting mechanism, even return codes. It wouldn't be any more of a valid criticism of them, either. The problem lies with the particular code that is mis-using the mechanism, not the mechanism itself.

They ought not, obviously, so I have to write code to wrap all of these checked exceptions in unchecked exceptions.

You do not have to write more code because of checked exceptions (at least, in the way you are describing); you have to write more code because you chose to use a different error reporting mechanism than the one the data repository used. As you would if the repository used return codes or some other error-reporting technique. Such problems cannot be blamed on checked exceptions.

Tom Gibara - Re: The Trouble with Checked Exceptions  blueArrow
8/19/2003; 12:05:34 PM (reads: 2014, responses: 0)
I think that Anders Hejlsberg general thrust, that checked exceptions are problematic, is a valid observation. But I think his actual complaints about them miss the point.

Firstly, if one has the notion of checked exceptions as they occur in Java (which is the only style of checked exceptions I am familiar with) then it is obvious that they must be subject to versioning constraints of the sort he describes. To put it more concretely, if they were not part of the public contract how could their 'checking' be enforced?

I do not expect the type of a public method parameter to be changeable in compatible versions of a Java interface for precisely the reason that I know that the Java language performs static type checking. To complain that statically checked exceptions are effect interface versioning is just irrelevant. It's their purpose.

Secondly, I absolutely do not recognize the observations about developers writing "throws Exception" declarations to avoid dealing with declared exceptions. The two situations I have seen (and used) this pattern of development is

a) in reflective code (where there is no type-safety) b) in test harnesses (where one really does want any exceptions)

What is more common is the existence of catch(Exception ...) clauses which trap any type of exception and even (*shudder*) catch(Throwable ...) which will even catch out of memory conditions etc. This is certainly bad practice in general. Because in an instance where you have a call stack spanning three modules A B C, then if a class in C throws a specific Exception for A, but B catches it indescriminately then unexpected behaviour may occur. This has nothing to do with checked exceptions.

I agree about the observations about Exception recovery (eg. in the form of finally clauses) being more important than exception handling. Of course the type of an Exception can be important in determining the nature of recovery. If, when my code attempts to access a resource, I get a BusyTryAgainException my code will probably recover differently than if it received a IAmDeadException. Again this has nothing to do with whether exceptions are checked or not.

The final comments on the scalability of checked Exceptions are quite bogus. To quote:

"Each subsystem throws four to ten exceptions. Now, each time you walk up the ladder of aggregation, you have this exponential hierarchy below you of exceptions you have to deal with. You end up having to declare 40 exceptions that you might throw. And once you aggregate that with another subsystem you've got 80 exceptions in your throws clause"

This idea is preposterous and I have never seen exceptions used in this way. Each interface throws exceptions which are pertinent to the function it may be asked to perform. While it is true that in an aggregated system there may be many many underlying sources of failure, these different modes of failure are rarely relevant to the client code of a package.

If I were to write a data storage interface to persist objects. It might have several implementations, perhaps one might be backed by a database and deal with SQLExceptions, another might be disk based and deal with IOExceptions, but both would present the same public DataFailureExceptions.

My irritation with the article is that presents (in my opinion) an accurate observation, but then goes on to say nothing of relevance to it whatsoever.

Tom Gibara - The Real Trouble with Checked Exceptions  blueArrow
8/19/2003; 12:06:03 PM (reads: 2019, responses: 0)
The actual problem concerning checked exceptions is very simple. Simply: it is not the responsibility of an interface to declare which exceptions must be dealt with by the code which uses it.

The most simple example is the java method:

Integer.parseInt(String str) throws NumberFormatException

where NumberFormatException is a checked exception. This obligates me to catch the exception on each call to the method, even though I may not want to. Even if I know in advance that the exception cannot be thrown, as in the following trivial example:

try { int i = Integer.parseInt("120") } catch (NumberFormatException e) { /* I know I can't get here! */ }

I have not spent much time thinking of possible alternatives. But perhaps what is really needed is a global static code analysis which checks that, no matter which method calling pattern occurs within a program, every exception type which may occur is caught at some point. I have no idea whether this is a feasible proposition.

Vesa Karvonen - Re: The Trouble with Checked Exceptions  blueArrow
8/19/2003; 2:30:50 PM (reads: 1971, responses: 0)
it is not the responsibility of an interface to declare which exceptions must be dealt with by the code which uses it.

IMO, that is very well put! I have to say that I completely agree with this principle (unless someone manages to convince me otherwise).

I absolutely do not recognize the observations about developers writing "throws Exception" declarations to avoid dealing with declared exceptions.

I have used the technique systematically in some of my Java code, because I would rather let exceptions propagate to appropriate handlers than eat them or write intricate code that translates checked exceptions to unchecked exceptions and then later recovers the original exception in the handler.

Pseudonym - Re: The Trouble with Checked Exceptions  blueArrow
8/19/2003; 4:51:39 PM (reads: 1936, responses: 0)
The actual problem concerning checked exceptions is very simple. Simply: it is not the responsibility of an interface to declare which exceptions must be dealt with by the code which uses it.

I agree with Vesa. This is an excellent way of putting it.

IMO the right way to think about what an "interface" is that it's the set of assumptions that I have to make about your code in order to write my correct code. Sometimes I care about what specific exceptions that you will throw, say, if I intend to correct the problem. Some of the time I don't and just want to report the problem's existence, such as when the problem is due to user error.

Which is more important depends on the problem being solved (e.g. in safety critical systems, attempting to correct some classes of problem may be very important), but I am willing to bet money that the second situation is far more common. Language buffs know this all too well, because we've all used compilers which tried to be smart about correcting errors in our code only to get horribly confused.

I wouldn't say that checked exceptions are useless, but I do think they optimise for the uncommon case.

Dan Shappir - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 1:11:04 AM (reads: 1887, responses: 0)
I believe the main source of the problem is the conflict between OOP, with encapsulation being one of its major tenants, and exceptions.

On the one hand encapsulation dictates the calling code knows as little as possible about the implementation details of the code being called. That is, you push the details down as much as you can.

With exceptions OTOH you want to handle them only at a level that has sufficient context. This generally means pulling them up almost as much as you can.

The result of the conflict is either that implementation details leak, thus breaking encapsulation, or the exception handling is pushed down, loosing much the benefit of this mechanism, or exception handling becomes wholly generic, again loosing much of its benefit.

I don't have a good solution for this conflict. I tend to agree with Daniel (as I understand him ;-) about the added value of being able to use interfaces as exception specifications, and catch interfaces. This is especially pertinent for Java and C# that don't support multiple implementation inheritance.

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 5:39:49 AM (reads: 1833, responses: 2)
The result of the conflict is either that implementation details leak, thus breaking encapsulation, or the exception handling is pushed down, loosing much the benefit of this mechanism, or exception handling becomes wholly generic, again loosing much of its benefit.

I don't see this conflict. How an exception is handled is almost always independent of the details of the the exception itself in my experience.

Could you provide an example of this conflict?

Dan Shappir - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 6:21:23 AM (reads: 1839, responses: 1)
Could you provide an example of this conflict?

Pseudonym gave the explenation:

Strongly statically checked exceptions fundamentally break abstraction, because any change in implementation will, in general, change the possible set of exceptions which may be thrown.

For example, a method may be using a third party lib that throws a specific set of exceptions. Since you don't want to handle these exceptions in that method (it doesn't have sufficient context to handle them, e.g. user intervention may be required and this method doesn't do UI) you let them propegate out. A new lib comes out that provides similar functionaility but throws a different set of exceptions. You update to that lib because of improved performance/security/reliability. You must now update the method exception specification.

Not only that, you must now update the method's clients, even though encapsulation should mean that they don't need to care which lib it uses.

Johannes Brodwall - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 7:14:34 AM (reads: 1808, responses: 0)
I agree with the gist of Tom Gibara's post, but to be nitpicky, NumberFormatException is an unchecked exception, so you are not forced to handle it.

However, UnsupportedEncodingException, CloneNotSupportedException, MalformedURLException, and a few others are checked, and should definately not be. These three alone have caused me enormous amounts of pain. And why does InetAddress.getLocalHost throw a checked exception?!

The problem with checked exception is that you give the API designers the possibility of enforcing correct behaviour from API users. However, as the J2SE Standard API shows, not even seasoned developers can do this perfectly. Then you have the mistake of one API designer hurting a lot of developers.

If we could trust everyone to be perfect, I would be all for checked exceptions. But then again, if everyone was perfect, we probably wouldn't need them.

Harald Holtmann - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 9:50:48 AM (reads: 1774, responses: 1)
In my view the problem with exceptions is that they are used for two completely different purposes:

- for signalling problems, i.e. to signal (external) circumstances that abort the current action. The only possible response is to retry the intended action in a different way or simply to tell the user/client/system that the action could not be performed.

- as additional return-value. This can be used if the returned type is not appropiate for some border cases. It is expected by the library author that the caller more or less immediately catches the exception.

A example for the latter case is the Integer.parseInt method (not parsable -> NumberFormatException is thrown). Here a checked exception is useful, it documents the additional return value. Of course it would be better to use an appropiate return value (e.g. parseInt :: String -> Maybe Int) to spare the user the hassles of exception handling, because the caller has the catch the exception in any case, either to use the additional return value, or to convert the check exception into an unchecked one to indicate an abortion.

In practice, there are a lots of application of exceptions without this clearcut distinction. So, I think, the clear 'additional return' cases should use a better return type and all others exception should not be checked.

Perhaps AOP could be used to shift the usage of checked exception in JAVA, where one is bound by a multitude of APIs, to unchecked ones.

Kory Markevich - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 9:51:28 AM (reads: 1811, responses: 0)
Since you don't want to handle these exceptions in that method you let them propegate out.

And that, I believe, is the real problem. The original exception should not be propagated beyond layer boundries. The exception from the third-party library that is the source of the problem is only of interest to a human debugging, not to the client code of your method.

The possible ways that a software component may fail is part of the interface of that component, whether specified via check exceptions or not. The breaking of encapsulation occurs when you let an implementation detail (third-party exception) become visible to clients of the component (by simply propagating). Checked exceptions throw these cases right in your face, but such issues still exist with unchecked exceptions or even return codes.

I find discussions about checked exceptions inevitably come down to the same issues as static vs. dynamic typing. Those developers who do not want (or do not understand how to use) the compiler's help with error reporting will not like checked exceptions. For such developers checked exceptions are as much an obstacle as type annotations are to those who prefer dynamically typed languages. It is unfortunate that Java's fairly simplistic type system makes dealing with exceptions more cumbersome, as does the way it clutters the exception mechanism with issues that could be handled in other, better ways (NullPointerException, ClassCastException, etc.). The standard library is also full of poorly designed exceptions (IOException, SQLException).

Some sort of error reporting mechansim for dynamic errors is always necessary. As a fan of static typing, I prefer to have the compiler assist me as much as possible. Fortunately for others Java does not impose any requirement to use checked exceptions beyond what is present in the standard libraries. Other methods such as unchecked exceptions and even return value encoding are still available.

An interesting point with Java is that the language syntax makes ignoring an exception require nearly as much work as handling one. I wonder how many people's complaints would go away if just this was made simpler?

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 2:29:46 PM (reads: 1702, responses: 0)
Pseudonym gave the explanation:

Strongly statically checked exceptions fundamentally break abstraction, because any change in implementation will, in general, change the possible set of exceptions which may be thrown.

I agree there is a problem with static checking. I think this is separate from what Dan wrote in another message...

I believe the main source of the problem is the conflict between OOP, with encapsulation being one of its major tenants, and exceptions.

That is, I do not believe there is a conflict between OOP and exceptions. Exceptions are encapsulated. All you know about is the name.

The problem is not encapsulation. The problem is having to know statically the names of the encapsulated exceptions that will come your way.

These are two different issues.

Albert Y. C. Lai - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 2:32:04 PM (reads: 1733, responses: 0)
Exceptions as poor man's anonymous sum type:

class Nil extends Exception
{
}

class Cons extends Exception { private Object x; private ListThunk s; public Cons(Object x, ListThunk s) { this.x = x; this.s = s; } public Object x() { return x; } public ListThunk s() { return s; } }

interface ListThunk { public void s() throws Nil, Cons; }

interface Transform { public Object f(Object x); }

class Map implements ListThunk { private Transform f; private ListThunk l; public Map(Transform f, ListThunk l) { this.f = f; this.l = l; } public void s() throws Nil, Cons { try { l.s(); } catch (Nil n) { throw n; } catch (Cons c) { throw new Cons(f.f(c.x()), new Map(f, c.s())); } } }

public class Nightmare { public static void print(ListThunk l) { try { l.s(); } catch (Nil n) { } catch (Cons c) { System.out.println(c.x()); print(c.s()); } } public static void main(String args[]) { ListThunk l = new Map(new Transform() { public Object f(Object x) { return ((String)x).toLowerCase(); } }, new Cycle(args[0])); print(l); } }

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 2:50:13 PM (reads: 1778, responses: 0)
Let me illustrate my previous point with an analogy.

Consider an application command processor. The application has many functional areas. Each functional area has multiple commands that can be processed.

I can define a command abstraction and a command processor that works on the abstraction, independent of the specifics of any one command and any functional area.

In this case the commands are encapsulated and the command processor only has to be written once. New commands can be added at any time.

Now say the commands have names. And say that optionally a command can be designated as "checked", i.e. that at compile time the command processor must declare its awareness that some functional area is providing such commands.

Has encapsulation been broken? No. The problem is that named encapsulations are checked at compile time.

Is this a good thing? You decide the value.

Does it have a cost? Yes. The command processor has to be updated whenever a checked command is added to the system.

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/20/2003; 3:00:04 PM (reads: 1720, responses: 0)
Exceptions as poor man's anonymous sum type...

Yes, we should remember that throw and catch are just syntax.

Michael Feathers - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 4:42:42 AM (reads: 1623, responses: 1)
It is funny that we can easily drop checked exceptions and not feel pain, yet we don't reconsider the rest of static typing. So far I have not heard a single complaint from a C++ or C# programmer about the lack of checking.

Frank Atanassow - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 5:04:13 AM (reads: 1605, responses: 0)
try {
  int i = Integer.parseInt("120")
} catch (NumberFormatException e) {
  /* I know I can't get here! */
}

IMO, using this as evidence that exceptions should not be mentioned in the interface is bogus because it lays blame in the wrong place. If the type of Integer.parseInt says that it may raise NumberFormatException, then this implies that you should only be using parseInt in places where the argument is not known to parse as an integer. If, as in this case, you happen to know that the argument does, you should use a different method, for example supply the Integer directly as a literal: "120"I or something. Java (AFAIK) does not provide such literals, and that is rather the language defect which needs to be fixed here.

The reason I think exceptions should be mentioned in the interface is because the way to represent a partial function as a total function is to make the return type a sum. In Haskell, for example, one would write:

parseInt :: String -> Maybe Integer
which type is isomorphic to
Either Integer ()   (= Integer + 1)
A language which allows exceptions ought to relate (pure) functions of this type with (impure) exception-raising procedures. In languages founded on classical logic, there is something quite close to this, called cocurrying: given a function of type
A -> C + B
by supplying a B-continuation
B -> 0
one obtains a residual
A -> C
The behavior is that if you supply a value of type A which produces a value in the left summand, it is returned; if you supply a value of type A which produces a value in the right summand, it is passed to the continuation.

(Actually cocurrying is a bit different from this but this is the basic idea.)

Kory Markevich - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 9:17:48 AM (reads: 1620, responses: 0)
So far I have not heard a single complaint from a C++ or C# programmer about the lack of checking.

Then consider this your first. :)

I find exceptions in C++ to be next to useless. C++ exception specifications do nearly the complete opposite of what I want. The compiler is of no help in finding cases where I've missed a catch, so avoiding all exceptions being caught in main( ) requires an inordinate amount of effort (I've seriously considered writing a code analyzer to essentially mimic checked exceptions). And because of the object model/memory handling, chaining exceptions is extremely inconvenient.

I haven't any experience with C#, though. I find it an odd case since it's GC would make chaining as easy as in Java, yet the exceptions are still completely dynamic in nature.

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 9:29:24 AM (reads: 1576, responses: 0)
parseInt :: String -> Maybe Integer

One way to do this in Java is...

public Integer myParseInt(String s) {
    Integer result = null;
    if s parses correctly {
        result = the parse integer;
    }
    return result;
}

Just as with Maybe in Haskell you have to handle both cases, in Java the two cases can be null or an Integer.

(Then you could use the Null Object pattern, etc. That is just an elaboration of this example.)

Granted that Haskell provides more support than Java. But this just illustrates that checked exceptions is one solution to a problem. Choosing it does not solve all your problems. Does it solve a problem well enough? Not to overcome the cost of maintenance, I think.

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 11:24:23 AM (reads: 1557, responses: 1)
C++ exception specifications do nearly the complete opposite of what I want. The compiler is of no help in finding cases where I've missed a catch...

But you have to remember that there will be unchecked (so called "runtime") exceptions that have to be handled. The Java compiler which does point out undeclared checked exceptions, still does not tell you about runtime exceptions.

It's all about communication cost/benefit. Tools could exist which do whole-program analysis to find uncaught exceptions, without putting a burden on development speed... i.e. run the analysis on a whole program when it makes sense to the team, as opposed to running it every time you want to compile a class during development.

Kory Markevich - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 1:02:36 PM (reads: 1569, responses: 0)
But you have to remember that there will be unchecked (so called "runtime") exceptions that have to be handled. The Java compiler which does point out undeclared checked exceptions, still does not tell you about runtime exceptions.

Of course. I'm not arguing, for example, that division by zero should result in a checked exception. There will always be a class of errors that prove inappropriate for any error reporting scheme. But if the developer of a routine I call declares that it may fail, that it will signal that failure by throwing a certain exception, and the price of me forgetting (or even worse simply mis-handling it) is to have the entire application come to a halt, then I want to know as soon as possible so that I can fix the problem. As with other types of static analysis, this is something the compiler can do and do well; I simply want to take advantage of that.

While I'm not convinced that unchecked exceptions are the best way to handle some errors (though I don't have any alternatives to suggest), I simply don't understand how the existence of such errors could be an argument against using checked exceptions for signalling others.

It's all about communication cost/benefit. Tools could exist which do whole-program analysis to find uncaught exceptions, without putting a burden on development speed... i.e. run the analysis on a whole program when it makes sense to the team, as opposed to running it every time you want to compile a class during development.

Yes, and you could run unit tests when it makes sense instead of having the compiler warn you of type errors. It was never my intention to convince people, least of all you, to use checked exceptions or that they were the best mechanism. As I aluded to earlier, whether you like checked exceptions often comes down to how much static analysis you want in the language and compiler. What really irks me about checked exception debates is that they are inevitably based upon misunderstanding of the mechanism (e.g., propagation versus chaining), poor examples (Java libraries), and simple preference. Though I guess that last one is inevitable. :)

Burdens on development speed is a very subjective factor. For example, I prefer to compile as often as possible (the way Eclipse does Java fits perfectly with my style). For me, the less code written before a possible design flaw is detected, the less code I have to go back and fix. I find that having to trace exceptions, whether manually or with a separate tool, is a burden to my development speed. Not tracing them is a burden to my code quality. Ultimately, my preference is for it to be cheap to find potential errors in my code and take some effort to ignore them, than for it to be difficult to find potential errors and effortless to ignore them.

Vesa Karvonen - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 1:09:44 PM (reads: 1554, responses: 1)
I find exceptions in C++ to be next to useless.

It has been over a year since I last seriously programmed in C++ (or over two years depending on how you count "serious"), so these things are not too fresh in my memory, but the way I remember it, the primary (not the only one) intention behind the use of exceptions in C++, in my programming, has been to ensure (to essentially prove statically) that as soon as an error is detected, it is guaranteed that the normal path of computation is not continued. C++ exceptions make this very simple. I just write

if (error_condition(...)) throw error_condition_exception(...);

and make sure that all code is as exception safe as is meaningful (ok, that takes some effort, but it is not too difficult). To me, other kinds of error reporting techniques, available in C++, seem much more tedious and difficult to use.

Actual handling of and recovery from a particular exception is almost always only a secondary concern, unless the failure to handle and recover from the exception effectively means that the program does something that compromises valuable user data. For example, if the user chooses "save and quit" and the save fails (throwing an exception) then it is imperative to not simply quit. On the other hand, if all user data is stored in persistent storage, and an attempt to save new data computed by the program fails, then it is almost always ok to simply terminate the program with an error message as a first approximation of correct behavior. If the simple termination turns out to cause significant grief or it is easy to recover from the exception, then it is appropriate to handle the exception and recover when it is possible.

(I've seriously considered writing a code analyzer to essentially mimic checked exceptions).

One of the biggest problems in C++, IMO, is the daunting complexity of the syntax. I personally really wouldn't want to write a parser for C++. However, conceptually, some conservative effect inference for exception specifications should not be too difficult. I would personally like to see type and effect inferred languages in which exception specifications were part of the inferred annotations of functions.

And because of the object model/memory handling, chaining exceptions is extremely inconvenient.

Yes, chaining of exceptions in C++ is indeed very difficult, unless the base exception classes are explicitly designed for it. To enable chaining, the base exception class should have a virtual clone() operation.

However, I don't ever recall being in a situation in which I would have specifically wanted to chain exceptions in C++. Of course, there may be several reasons for this. I guess that except for few specific situations, I don't really bother to analyze the complete set of exceptions that may actually be propagated through a particular piece of code. I've never been involved in developing anything where the first approximation of termination with an error message would have been too costly (of course, I don't mean that termination would have always been appropriate as a second approximation).

Bart van der Werf (Bluelive) - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 1:28:21 PM (reads: 1543, responses: 0)
This is something that im including in my own compiler project. Its basicly the same as the java exception system.

try { ... } silence (SomeException)

Causes a exceptions to be rethrown, but not requiring them to be declared thrown by the method.

This way exceptions that are normally considerd checked exceptions but that are not handable within the scope of the current applicaton, to be retyped to unchecked exceptions.

because checked exceptions are something that only is checked in the validation step of compiling this has no effect on the resulting code.

Another intresting thing is what effect generics or parametrics could have on exception usage.

Kory Markevich - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 2:08:59 PM (reads: 1562, responses: 0)
Actual handling of and recovery from a particular exception is almost always only a secondary concern...

Interesting point. I was judging C++ exceptions by how well they assist me in signalling and handling errors. I wonder how much weight the C++ committee actually gave to each of those concerns.

I personally really wouldn't want to write a parser for C++.

Nor did I. I was able to find a number of C++ parsers on the web, though most had issues with some of the more recent features (namespaces, etc.). In the end it came down to me not wanting to write the analyzer using C++, which is what the most capable parsers were written in.

Yes, chaining of exceptions in C++ is indeed very difficult, unless the base exception classes are explicitly designed for it. To enable chaining, the base exception class should have a virtual clone() operation.

That's exactly what I ended up doing in one project. Very cumbersome.

It's interesting how people's experience with C++ exceptions have coloured the acceptance of checked exceptions in Java. For C++, the usual mantra is throw when there's an error, catch when you can handle it, and propagate it otherwise. Doing the same thing with checked exceptions (as many Java books did and probably still do recommend) creates numerous problems. Some, like breaking encapsulation, existed even in C++ but weren't as easily recognized. And given how the Java standard libraries were mostly written before a better understanding of checked exceptions was available, most developer's primary source of checked exceptions is more of a showcase of how not to use them. It's unfortunate that the most common response so far appears to be the abandonment of checked exceptions.

Vesa Karvonen - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 3:00:01 PM (reads: 1497, responses: 1)
hmm... What I would really like to see is a realistic (scale is the issue) example of where checked exceptions are arguably an improvement over unchecked exceptions without significantly increasing programming burden. So, if you think you have such an example, then please help me out.

...

It seems to me that exception specifications in most languages simply do not provide useful static guarantees.

I would like that when I explicitly annotate a function with a particular exception specification, it would mean that the compiler would statically verify that the function can not throw any exception other than the ones that appear in the exception specification. In other words, the compiler would verify that the function handles all other exceptions and does not propagate any of those exceptions to the caller. This would essentially allow me to specify exception barriers in my code that would be verified statically. By default, the compiler would simply conservatively infer the set of exceptions that might be thrown from a function. For higher order functions, for example, the exact set of exceptions that may be thrown depends on the functions passed as parameters.

The checked exceptions of Java are pretty much the exact opposite of what I would like to have. They force me to manually annotate every function with an exception specification. (The annotations are not inferred by default.) What is even worse is that an exception specification essentially does not guarantee that other unchecked exceptions might not be thrown. (The specification does not guarantee a useful property.)

I think that annotations that provide no useful static guarantees are best avoided. This doesn't mean that all such annotations would have to be (automatically) verified statically. It only means that after the annotations have been explicitly and correctly specified, then some useful static property follows.

Josh Dybnis - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 3:25:00 PM (reads: 1517, responses: 0)
From Graydon Hoare's weblog:

I think what lexical exception checking -- if you believe in it at all -- ought to do is specify which exceptions will not be thrown from a method.

Pseudonym - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 4:44:34 PM (reads: 1493, responses: 1)
What I would really like to see is a realistic (scale is the issue) example of where checked exceptions are arguably an improvement over unchecked exceptions without significantly increasing programming burden. So, if you think you have such an example, then please help me out.

There are such examples, as I mentioned previously. In safety-critical (or some kinds of mission-critical) systems, when you come across an undesired event, it is often important to try to correct the problem.

The "grandfather" paper, from which modern exception systems are descended, is Parnas and Würges, "Response to Undesired Events in Software Systems". That paper came out of Parnas' work on the A-7E aircraft Operational Flight Program, which is an excellent example of a mission-critical (not sure if it counts as safety-critical) software system. It had to deal with all sorts of bizarre pieces of hardware and be able to cope at any moment with something failing and take corrective action if necessary. This sort of system is exactly the kind of situation where checked exceptions are precisely what you want.

In most desktop/server/etc software, correcting problems is not so important, because some combination of reporting and recovering is usually sufficient.

Vesa Karvonen - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 5:37:58 PM (reads: 1463, responses: 0)
In safety-critical (or some kinds of mission-critical) systems, when you come across an undesired event, it is often important to try to correct the problem.

Yes... It is indeed reasonable that in some situations you really have to know that you have handled certain (or all possible) exceptions. In such a case all the exceptions that can (and should) be recovered from should be checked exceptions.

One question that comes to mind is that whether being checked or unchecked exception is a universal property. Specifically, if there are two applications A and B and they have sets of exceptions that should be checked Chk[A] and Chk[B] and sets of exceptions that do not need to be checked Unc[A] and Unc[B], then does it follow that the intersection of Chk[A] and Unc[B] is always the empty set (and symmetrically for Chk[B] and Unc[A]).

Pseudonym - Re: The Trouble with Checked Exceptions  blueArrow
8/21/2003; 5:38:13 PM (reads: 1454, responses: 0)
Yes, we should remember that throw and catch are just syntax.

I'm not sure if that was meant to be sarcastic or not.

"Throw" and "catch" are on one level syntax and semantics, but on another level are pragmatics. They imply a performance model (e.g. language implementations usually optimise for the case where exceptions are not thrown) but, more importantly, they also imply programmer intention. Programmers know that exceptions are almost always for signalling undesired or unusual events, not "normal" events. If a typical programmer sees that some code throws an exception, they will assume that this is not intended to be the usual return mechanism from the function/method in question. If you don't want a programmer to make this assumption, use another mechanism.

Frank Atanassow - Re: The Trouble with Checked Exceptions  blueArrow
8/22/2003; 4:50:47 AM (reads: 1405, responses: 0)
Just as with Maybe in Haskell you have to handle both cases, in Java the two cases can be null or an Integer.

And what about Maybe (Maybe Integer) ? :)

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/22/2003; 9:45:24 AM (reads: 1368, responses: 0)
And what about Maybe (Maybe Integer) ? :)

It gets more cumbersome, doesn't it?

But in the end it's just an object...

public class MaybeMaybeInteger {
   ...
}

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/22/2003; 9:51:10 AM (reads: 1372, responses: 0)
It was never my intention to convince people, least of all you, to use checked exceptions...

I'm nailed. 8^(

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/22/2003; 10:05:34 AM (reads: 1381, responses: 0)
more importantly, they also imply programmer intention

Agreed. I wasn't trying to be sarcastic. I just liked the example.

Ehud Lamm - Re: The Trouble with Checked Exceptions  blueArrow
8/24/2003; 4:00:37 AM (reads: 1299, responses: 0)
The "grandfather" paper, from which modern exception systems are descended, is Parnas and Würges, "Response to Undesired Events in Software Systems".

This is an important paper. Is anyone aware of an online copy, outside the ACM digital library?

Pseudonym - Re: The Trouble with Checked Exceptions  blueArrow
8/24/2003; 5:04:05 PM (reads: 1249, responses: 1)
This is an important paper. Is anyone aware of an online copy, outside the ACM digital library?

Not that I know of, but if you're in the mood to spend some money, or have a well-stocked library at your disposal, you may wish to locate a copy of Software Fundamentals: Collected Papers by David L. Parnas. (Disclaimer: I'm not associated with the publishers or author(s), I'm just a satisfied reader.)

wh - Re: The Trouble with Checked Exceptions  blueArrow
8/24/2003; 10:26:53 PM (reads: 1239, responses: 0)
I think a lot of this discussion misses Hejlsberg's main point -- that programmers (in general) tend to code up the most basic functionality to get things going, and stub out the edge cases for later (perhaps when they understand them better). C programmers were notorious for failing to check return codes. (I recall an HP vs Sun benchmark years ago where HP just couldn't seem to beat Sun's performance numbers -- until they analyzed what what Sun's code was doing and found an unchecked error was bailing early, avoiding the bulk of the work in the inner loop, and making the benchmark "run much faster".) And I don't know how many Java programs I've seen that say:

try {...} catch (Throwable e) { /* log it... */ }

Although this avoids some class of errors from occurring in the calling code, it doesn't in general handle all the edge cases properly. Often times this amounts to no user feedback for a failed operation (when there should be some), or failure to take an appropriate follow-on action.

I think that the OO/hierarchical nature of exceptions is both a blessing and a curse in this regard. It's a blessing in that implementation subclasses can throw subclasses of exceptions declared in a superclass or interface (thus avoiding violating encapsulation), but it's a curse when unintended capture occurs (as in the above example).

Perhaps what's really needed here is better compiler support for development vs. production code. I.e. in "development" mode, exceptions would be unchecked by the compiler thus allowing the programmer to quickly get a prototype running, but in "production" mode, the compiler would require strict handling of exceptions, either catching or declaring them as raised by the method signature. To accomplish this, we would need to also eliminate the notion of RuntimeExceptions (forcing them to be dealt with in production code, perhaps just by aborting the program), and eliminate the notion of hierarchically defined exceptions (to avoid catch-all handlers).

Let's face it, the ability to catch a class of exceptions (union of various specific exceptions that are thrown) is really only used out of laziness (not that laziness is always bad -- which is why I've suggested a development mode). However, if the ability to define exceptions hierarchically is really that useful, we could just restrict catch clauses to only catch 'final' exception classes (in production mode).

Warren

Ehud Lamm - Re: The Trouble with Checked Exceptions  blueArrow
8/24/2003; 11:44:58 PM (reads: 1257, responses: 0)
I don't have the book here, but I skimmed it, an I am a fan of Parnas's early work. So I'd second the recommendation for this book.

Patrick Logan - Re: The Trouble with Checked Exceptions  blueArrow
8/29/2003; 10:34:17 AM (reads: 1135, responses: 1)
This is an important paper. Is anyone aware of an online copy, outside the ACM digital library?

Here is a PDF of the paper.

Ehud Lamm - Re: The Trouble with Checked Exceptions  blueArrow
8/29/2003; 2:07:26 PM (reads: 1135, responses: 0)
Patrick, how about posting it to the home page?