Arnt Gulbrandsen
About meAbout this blog
2009-11-12

Exceptions, and exceptions, and exceptions

Exceptions are such a pain. They look as if someone thought they could solve many disparate problems with one tool, and in the end, the tool doesn't look terribly elegant, and people don't use it terribly well.

Exceptions as part of an API. This is good. In java terms, you publish the exceptions as part of your package, and document them, and use them, and you have to handle exceptions in the packages you use.

This use breaks down when exceptions are republished, as they often are. If class com.example.Foo has an implementation which uses br.com.exemplo.Bar, then Foo shouldn't re-export Bar's exceptions. Those exceptions are part of Foo's implementation, not of its interface, and random implementation details do not belong in an interface. If nothing else in the interface says Foo uses Bar, what justifies having exceptions from Bar there?

An implementation detail should only be published in the interface if there is some specific reason for that. I venture to suggest that if that is the case, then it has an interface-specific name. For example, if the implementation opens a file as part of initialising an object, then exporting java.io.FileNotFoundException is bad, and exporting your own exception (e.g. com.example.ResourceUnavailable) is the right thing do do.

Exceptions in order to shift unusual code away, ie. to allow bottom-level code to handle the common case readably, shifting error handling elsewhere in the same module.

Fine, exceptions will do this, too. In this case exceptions are used to organise an implementation, which is desirable, but which does not justify exporting that exception out of the module (or package, or what have you).

So you may decide to handle a java.io.FileNotFoundException anywhere in your implementation, but reexporting it in an interface which doesn't (otherwise) use files is bad. If you need to reexport, you should wrap the exception in an exception which uses the same kinds of resources and names as the rest of your interface. Doing so turns the exception into an API exception, as above.

Exceptions from the machine itself, ie. as a replacement for segfaults, memory allocation failures, etc.

I can't really say much about that. How does one handle these? Sometimes it's best to reexport under a different name, but generally these events aren't considered by the programmer, and only fools pretend to properly handle events they've never considered. So... I'd like static analysis to reveal where such events are possible, can't say more.

All the languages I know confusify these three, and don't really try to guide programmers towards using each kind sensibly. C++ does worst, since it has segfaults and exceptions. Feh.

PS. Then there's java's Throwable. What is that? Looks like a generic concept with no real purpose other than catering to some future possible need.

PPS. Quoting my favourite RFC: It is always possible to aglutenate multiple separate problems into a single complex interdependent solution. In most cases this is a bad idea.