Application letters at the Haskell workshop: suggestion

Alastair David Reid
17 Sep 2001 20:13:04 -0600

Marcin 'Qrczak' Kowalczyk <> writes:
> Parsec [uses some variant of the error monad] and similar things. It
> tries to generate reasonable messages of the form "expecting foo,
> found bar" or "unexpected bar" annotated with source position,
> making use of labels of higher level syntactic constructs inserted
> in the grammar as well as individual characters matched.

I think this illustrates an important point about different approaches
to exception handling.

Parsing is a great example of where error monads are useful:

1) You expect the errors to be the common case instead of the
   very unlikely case (so you're willing to expend quite a bit
   of effort to handle them well).

   Typecheckers also fit into this category.

2) You really care about what the error message looks like.

3) Your code is either all machine generated (e.g., by happy)
   or you use combinators (e.g., >>= and return) so it is easy
   to thread the error monad through and to be consistent about
   doing it.

In these case, I think error monads are the best choice.

The Hugs/GHC exception catching that Andy Moran described is aimed at
situations where these don't apply.  Cases include:

1) Your program has to manipulate some real world (and stateful)
   object and it is not considered acceptable to leave it in some
   confused state.  Examples include leaving windows open when a GUI
   equipped program crashes, a control system (like the joystick in a
   plane or controls in a lift) that suddenly stops responding, leaving
   a database in an inconsistent state, etc.

2) You write a library (e.g., Fran, HGL, etc.) where (hopefully)
   carefully written library code (which can be as full of error 
   checks as you want) has to invoke user code and, somehow, recover
   and, either keep going or shut down cleanly.

3) Someone gives you a great library but their code doesn't use the
   error monad (or whatever) because the code was developed for a
   less demanding execution environment.  The library is large and
   rewriting it is daunting.

4) You think you've used the error monad consistently and avoided
   calling all those "unsafe" Prelude functions like "head", "tail",
   "minimum", and "div" but you've got no good way of checking and
   you want your code to be robust.

5) You really don't care much which exception you get - as long
   as you get one.

I think the two approaches complement each other rather well (but, of
course, I'm biased...).

Alastair Reid