
An Uneducated View of Checked vs. Unchecked Exceptions - fogus
http://marcorogers.com/blog/09-27-2009/a-noobs-view-of-checked-vs-unchecked-exceptions
======
jacquesm
Exceptions are now a 'way of life' for many different programming
environments, the simplistic view is that they allow your program to 'catch'
any unforeseen issues at a lower level in a graceful way.

The truth is that the 'handling' is often much less robust than it ought to
be, which in turn will lead to the hiding of problems at lower layers.

A simple example would be an I/O error when reading a file, if you 'handle the
exception' then you have done absolutely nothing to take care of the
underlying problem.

Personally I'm a huge fan of spending the extra time required when writing
lower level code to make sure that _all_ cases are handled properly, instead
of throwing an exception at the slightest sign of trouble.

A pet example of this is the UTF-8 and unicode conversion library, when
presented with an 'undefined' character lots of libraries will cause a whole
series of exceptions to be triggered because of a single character issue in
some data to be imported.

If such a library were defined 'properly' then you'd tell it what to do with
'bad' input, toss it, replace it with ##undefined character## (or any other
string you come up with) or, in case you want it, throw an exception. The
defaults should be sensible and should maximize productivity.

That way the user of such a library knows the library will handle the job with
a minimum of fuss.

I see exceptions mostly being used as a way to be absolved from further
responsibility from an issue, and that's really not the way they should be
used.

Run time errors are uniformly bad, exceptions really are a form of runtime
error and should be avoided as much as possible. Especially in lower level
code.

~~~
barrkel
I strongly disagree that exceptions are about catching unforeseen issues at a
lower level. Exceptions should almost never be caught. A well-written program
will have only a handful of exception handlers that don't repropagate the
exception; ideally only one, at the highest level of the event loop or request
handler.

As to your string decoder example, the sensible default should be to throw an
exception, with an easily available out to replacements etc. as you suggest.
String conversion across multiple code pages is an interesting example
precisely because data loss is so easy, and if the problems aren't raised
early in the data flow, it can be hard to figure out where it went wrong. The
"default" manifests itself in Windows programs as '?' - rather annoying to
debug when the first time you see it is on the UI, while other concrete are
plentiful on the web. A very common example is balanced "smart" quotes, which
frequently end up encoded in either the web page or the RSS feed in a
different encoding than that described in the HTTP header, or in the meta tag,
or according to the relevant standards, etc.

Yes, runtime errors should be avoided, but not by the lower-level code;
rather, the lower-level code should throw exceptions when error conditions
occur, but have ways and means for programs that care about the exceptional
conditions to not throw exceptions. This isn't always possible, though.
Consider TFA's example of file not found:

> _The caller can't recover from a file not found error_

The caller can test to see if a file exists before trying to read it, but non-
transactional interactions like this with the file system are subject to
races; the file might exist one microsecond and be gone the next. But it is
also obviously true that the caller _can_ recover from a file not found error.
Concrete example: consider a thumbnail generating service, which checks to see
if a cached thumbnail file exists before doing the expensive work of creating
it if necessary. Here, a file not found exception (perhaps caused by
concurrent cleanup of cache, for random operational reasons) is indeed not
fatal.

~~~
jacquesm
> Exceptions should almost never be caught.

That's exactly where the controversy lies.

The string conversion issue was brought up because I think it is an excellent
case of 'programmer knows best', but the exception causes the whole dataset to
be rejected, not just the one record out of 3.9 million that were fine. That's
obviously sub-optimal. There has to be a better way to deal with that sort of
thing, such as for instance an 'output' vector where data that couldn't be
processed can be sent, or a series of warnings / errors attached to certain
offsetts in your data. Then after running the conversion you can decide if you
want to drop all of it (which is what happens now) and fix the input data, or
discard those records (sometimes that is an option), or manually edit those
and then run the import on those few records again.

Because exceptions are a mechanism you can then 'use' them for whatever you
feel like. Just like you can use setjmp/longjmp in C for all kinds of creative
stuff you can use exceptions to change the dynamic of your program. And this
leads to some very difficult to trace code because exceptions break the normal
flow.

Almost all exceptions in for instance the python mysql layer need to be
handled by your application otherwise you will be exposing the user of your
application (or website) to a runtime error and that is not good, not handling
those is not really an option (unless you don't like your users much :) ).

So, in production grade software you'll find that almost all possible
exceptions are indeed handled, and if possible as close as possible to the
location of the error to make sure the fall-out of the problem is minimized.

Generally speaking, the higher up the handler the less information there is
about the context of the exception, the smaller the chance you can handle it
in a meaningful way.

This style of coding makes it not rare to see half the code to be about
exception handling, the other half actual 'productive' code. Especially Java
suffers from this very much.

~~~
barrkel
For your string example, a callback would probably be the most flexible
solution, but I still believe that not signalling corruption when it is found
by library code is irresponsible. Batch processing code will have a loop;
that's precisely where there _should_ be an exception handler, one that can
log that there were errors with this particular record, so that attention can
be brought in later. If the data isn't too important, then sure, minor
corruption can be ignored, but it would be a terrible default.

Taking your database interaction example, here are the kinds of situations I
could see, and responses:

* Failure to connect; this is pretty fatal, unless there's home-spun failover support, it should not be handled at a low level

* Query syntax / API error: this is a programming error, should be logged and the current process (request, whatever) aborted

* Constraint failure: choosing whether to handle constraints on the database side, or on the app server side, or both sides, is an architectural decision with tradeoffs. Ideally constraints should be portable, so that they can be transported all the way to the UI, and checked at every stage along the way. These constraint failures might be signalled through exceptions, but that's a tactical choice, and may not be the best one. Generally, exceptions only signal for the first error, but usually there is more than one constraint, and multiple actionable failed constraints should be propagated to avoid wasting the end user's time (consider the simplistic compiler which halts on first error).

My point is that exceptions should generally either be things you can't
reasonably handle, with a non-throwing "out" for situations where you _do_
expect to be able to handle the problem. If you expect to be able to work
around the issue, i.e. find a solution locally, low down in the stack, then
you probably shouldn't be catching an exception in order to do so, unless
that's impossible (like the race condition I mentioned earlier).

~~~
jacquesm
> My point is that exceptions should generally either be things you can't
> reasonably handle

That we completely agree on.

As for that race condition, I think that a situation like that could easily be
avoided by having a single task be responsible for maintaining that data
structure and to send it messages to update the state atomically.

That way you avoid the race completely.

Race conditions usually are a sign of multiple parties having access to a
single resource where you'd be better served having one 'handler' process in
charge of the data object that multiple parties want to access. It can then be
the arbiter of who has access.

If the changes are a-synchronous then you could use a 'fire and forget' style
queuing mechanism where the only thing that matters is that you know the
message was received, the more tightly coupled example you gave could be
handled reasonably elegant with a client-server model where the server will
handle only client at a time for a particular resource.

Data objects not being 'alive' enough is the cause of many race conditions, by
assigning a handler you achieve two things in one go, the first is an
abstraction of interaction with the data object to a series of messages to a
handler, the second the removal of race conditions and their 'normal'
solutions (such as locks).

~~~
barrkel
The race condition example applied to a pretty global "single resource": the
file system; and came from the example in the main article. In situations
where you have greater control over the resource, I agree that serialization /
independence of operations, whether by transactions, queuing, single arbiter,
etc., is better.

------
jerf
I make the following observation: Everybody I've seen arguing in favor of
checked exceptions in the past couple of years have argued about their
theoretical benefits. Everybody who is arguing against them have argued about
how they have not worked out in real life.

This pattern seems to recur a lot in programming debates, where people don't
seem to realize that it doesn't matter how wonderful your theory is if when
you try it out, it's wrong. That's where checked exceptions are.

The time for _theorizing_ about their effects on programs is long, long past.
Who cares whether they theoretically force programmers to write better code?
We now know that in reality, it actually results in worse programs the
majority of the time. When theory and reality disagree, _theory is wrong_. Why
anybody still talks about their theoretical design goals is beyond me.

As near as I can tell, the debate is over if you ignore the people who haven't
let their theories catch up to reality. If someone's got a success story
posted about how checked exceptions saved their program's bum, I'm interested
in the link, but I haven't seen one.

(I say it that way because when you have something enough people will read you
can always find someone to pop up and claim that "X is good", but I'm
specifically asking for arguments that already existed prior to this post, to
avoid selection bias effects.)

~~~
barrkel
First up: I'm not a fan of checked exceptions.

But on the other hand, I must point out: checked exceptions certainly force
you to address the issue of the exception being thrown, and as a result, the
failure mode that they prevent can't occur, so the evidence you're seeking is
more or less impossible.

On the other hand, checked exceptions have their own failure modes, down to
human behaviour: rather than having to rewrite every method declaration on the
call graph to throw the appropriate list of exceptions, some bad apples just
catch exceptions and do nothing with them. This shuts up the compiler, but
harms product quality.

IMO, the theory behind checked exceptions is actually fundamentally mistaken.
Exceptions aren't much good if you're constantly trying to catch them.
Exceptions work best if you write your code in a transactional style, more or
less assuming that an exception can occur at any moment, on any operation.
This can be tricky: it can force you to make or keep copies of structures when
doing large structural modifications; but this difficulty is not strictly due
to exceptions, as failures modes need to be dealt with in any case.

------
smanek
As a professional Java programmer, with some experience in various strongly
typed languages (MLs, Haskell), I really liked the idea of Checked Exceptions
when I first heard about them (and even when I used them in small projects in
school). In principle, it seems like a way to allow for stronger static
analysis and allows the compiler to make stronger safety guarantees. It also
forces programmers to deal with oddball cases - instead of just ignoring them
like we are prone to do with magic return values (a la C) or unchecked
exceptions.

The problem is it never works. There are two common strategies that I've
encountered to deal with 90+% of checked exceptions:

-Most code just bubbles the exceptions up the top layer (i.e., the UI), which clutters up every method signature along the way. The UI then has a big handler that catches all exceptions, and just logs an error and shuts down the program. Even on the off chance that the top-level could do something more productive, the problem is that the stack has been unwound too far and it's too late to fix whatever the problem is.

-The other common approach is to wrap every checked exception inside a custom unchecked exception that is just thrown. This is my preferred solution (at least it doesn't clutter up every method signature), but I have to explicitly convert every checked exception into an unchecked one - which is just a bit of a hassle.

I give Sun some props for trying out on a limb with a really cool sounding
idea. But I think most people have realized it doesn't work very well in
practice ...

------
russell
Bruce Eclkel's pretty much state the anti-checked-exception camp:
<http://www.mindview.net/Etc/Discussions/CheckedExceptions> There are very
good comments from both points of view. The blog was written several years
ago, so his solution is somewhat dated.

I think checked exceptions are a bad idea for a couple of reasons. First is
their most acclaimed benefit: they force you to deal with the exception
immediately, even if you have no solution for it. You have to decide whether
to catch the exception or add a throws clause, which changes the method
signature and breaks it for any users of your method. The the default decision
for many programmers is to log the exception and swallow it, intending to come
back later. I have had to dig through applications to find sql exceptions that
were caught but not dealt with.

The second big problem I find is the poor design of exception classes. It is
not uncommon to find a class that throws a half-dozen exceptions. Every time
you call a new method you have a new exception to deal with. Either it should
have a single exception with different error codes for the different cases, or
the exceptions should be subclassed from a master exception.

~~~
lucifer
"I think checked exceptions are a bad idea for a couple of reasons. First is
their most acclaimed benefit: they force you to deal with the exception
immediately, even if you have no solution for it."

The use of the word "immediately" informs your misunderstanding of the purpose
of a checked exception.

Checked exceptions are enforced at _compile time_ (thus "checked" exception).
There is no such thing as 'temporal immediacy' at compile time (unlike
_runtime exceptions_ ). There is potential for spatial immediacy (if we
consider the static call stack), but that is not what the compiler cares
about. (In other words, you can catch exception Foo at the call site and
delegate handling up the call stack by throwing a Bar (cause: Foo)).

A compiler enforced handling of an exception declaration insures that the
program handles the exception at some level in the call stack, as apparent to
the compiler.

This whole (endless) discussion regarding checked/unchecked very much mirrors
the static-typing/dynamic-typing discussion. You can either consider the
merits pragmatically, or, you can adopt a dogmatic (religious) point of view
that embraces one paradigm and dismisses the other.

~~~
barrkel
Programming languages shape the way programmers solve problems. To use your
terms, when a compiler forces one to deal with a problem with spatial
immediacy (i.e. checked exceptions), and the language uses spatial colocation
to represent temporal colocation to a greater or lesser degree (as most
imperative languages do), then the programmer is being encouraged, quite
strongly, to solve the problem in similar space and by consequence in similar
time.

~~~
lucifer
You make a very persuasive point, but I have reservation regarding the words
"encourage" and "solve".

I would think the fact of the possibility of mapping exceptions (according to
some abstraction order mirroring the layering of the overall system) calls for
"acknowledge" instead of "solve".

And compilers can help with robustness of programs, but their influence on the
robustness of programmers is not immediately evident. So perhaps "suggests"
instead of "encourage" for a subset of programmers?

~~~
barrkel
The failure modes of software written in C versus memory safe languages, with
particular attention to security and buffer overflows, persuades me very
strongly that languages (and their implementations, i.e. compilers) do indeed
influence the robustness of programmers. What is a programmer other than the
programs he writes?

------
jsankey
IMO almost all of the problems people have in practice with checked exceptions
are from poor API design rather than an issue with checked exceptions
themselves. The core Java APIs use checked exceptions for too many conditions
which are not recoverable - causing a whole bunch of maintenance pain for no
benefit. This is understandable considering that Java was the first mainstream
language to really exercise the concept.

If used judiciously, I think checked exceptions do have benefits. There are
classes of errors that are exceptional but also conceivably recoverable. The
fact that they are exceptional means it is important to draw attention to them
(they may otherwise be overlooked). The fact that they are recoverable
justifies requiring calling code to make a choice - handle or propagate the
error. Even in these cases, the code can get messy, but I see that as a fact
of life: writing robust code isn't easy.

------
dlsspy
I wrote a blog post a long time ago into a response of a big list someone made
of exception handling anti-patterns which (in my opinion) are made possible by
checked exceptions.

[http://www.rockstarprogrammer.org/post/2007/jun/09/java-
exce...](http://www.rockstarprogrammer.org/post/2007/jun/09/java-exception-
antipatterns/)

------
scotty79
Maybe it would be more useful to declare what types of exceptions method
handles. Declaring what types of exceptions methods (throws|receives and not
handles) should be optional. The way it is now is too much work and too little
gain in typical setups.

~~~
russell
IIRC that's what C++ does and I recently read that it's mostly ignored. Of
course a compiler or IDE could detect what is caught and generate the javadoc
equivalent. That would be useful.

------
adatta02
try{ somecode(); }catch(Exception ex){ // eh this will NEVER happen }

~~~
cema
Exactly! :-)

