[Haskell-cafe] Re: Exception handling in numeric computations

John Lato jwlato at gmail.com
Thu Mar 26 09:36:11 EDT 2009

On Wed, Mar 25, 2009 at 7:02 PM, Gregory Petrosyan
<gregory.petrosyan at gmail.com> wrote:
> First of all, thanks everybody for the discussion -- very interesting to read!
> Apologies if this is off-topic a bit.
> While reading, I have a feeling that proposed solutions are somewhat similar
> to checked exceptions. And IMO they turned out to be more harmful than useful.

Do you mean checked exceptions, or the proposed solutions?  IMO the
problem with checked exceptions is that they conflate two ideas by
trying to use the existing exception framework to do something that
can't otherwise be done by the language.  This leads to something that
doesn't work well for either use.

Languages with checked exceptions usually use them for two purposes:

1.  Exceptional conditions - disk full, CPU on fire, etc.
2.  Error handling - invalid arguments to a function, attempt to
invert non-invertible matrix, etc.

In the first case, checked exceptions are a pain because there are so
many different possible exceptional conditions to enumerate.  Or
alternatively you have an IOException that can be one of hundreds of
actual exceptional conditions.  This is exactly what regular
exceptions are for, and in general there's little that can be done
except terminate the program, except for certain special cases
explicitly handled by the programmer.

For the second case, checked exceptions actually work okay IMO.  The
syntax is a bit clunky, but they serve the purpose.  That is, they
specifically indicate what functions may fail and the failure mode.
They also allow calling code to either handle it there or pass the
error up to a higher level as appropriate.  The syntax is usually
ugly, though.

Combining these two functions into one tool gives suboptimal results.
If you're using checked exceptions for error handling, then you also
end up using them for exception handling.  That means you have
IOException thrown by just about everything (and in theory any
function in an imperative language could have an IOException due to
hardware fault), leading to complex exception handlers with a half
dozen case statements.

There are a few reasons why Haskell's approach is (or at least can be)
different.  Compare the error handling models.  Checked exceptions are
bolted on to an existing tool attempting to make it serve a different
purpose.  It seems like a good idea, but ends up being painful because
the two uses are at cross purposes.  Instead of using a specific
implementation and trying to alter it for another use, Haskell uses a
very general facility, the type system, to solve one problem, and
keeps the exception handling facility separate.

Haskell's syntax for error handling is also much nicer.  Nobody likes
using methods that could possibly throw a dozen different exceptions,
some of which are IOException and some are not.

Actually, Haskell IO makes a big difference to this.  Conceptually,
you could think of Haskell functions as purely mathematical
constructs.  In particular, they can't interact with the physical
world.  This is true even for the IO monad.  Exceptional conditions
only arise when the Haskell runtime actually tries to *evaluate* an IO
function.  As a consequence exceptional conditions only arise as part
of IO actions, and can only be handled by IO actions (because they're
interacting with the run-time environment).  This distinction makes it
possible to enforce a separation between exceptions and error
conditions, which is generally not possible in imperative languages
where a function could fail either for a computational reason (divide
by 0) or a hardware/runtime fault.

Even in Haskell this separation isn't absolute.  Programmer errors,
such as dividing by 0, can and do lead to exceptional conditions.  The
proper way to handle dividing by 0 is to not do it in the first place,
but if it happens because of a programming error, you've got an
exception.  Unfortunately this encourages programmers to think that
handling the exception is the proper way to deal with this condition,
but it isn't.

I've only recently come around to the camp of treating exception
handling and errors separately, so some of these thoughts may be a bit
loose for the moment.  In particular my thoughts from the above
paragraph have only recently become clear.

Henning T., FYI your constant advocacy has gotten at least one person
around to this view.

John Lato

More information about the Haskell-Cafe mailing list