There are too many error handling conventions used in library code!

Eric Kidd emk.lists at gmail.com
Sun Mar 11 09:13:30 EDT 2007


Donald Bruce Stewart <dons <at> cse.unsw.edu.au> writes:
> The lack of consistent error reporting between libs results in verbose
> code, as we're not able to use a single error handling technique when
> gluing together code from different libs (i.e. we can't just use Maybe
> or Either/ErrorT). 

Thank you for starting this discussion!

As you pointed out on IRC, the forthcoming cabal-install means that Haskell
programmers will tend to use more libraries in the future, making API
inconsistencies more of a nuisance.

The specific situation I'm trying to avoid is where libraries that are used
together have arbitrary exception-reporting APIs. For example, consider a
program to download a web page and parse it:

1. Network.URI.parseURI returns (Maybe URI).
2. Network.HTTP.simpleHTTP returns (IO (Result Request)), which is basically a
broken version of (IO (Either ConnError Request)).
3. Parsec returns (Either ParseError a)

So there's no hope that I can write anything like:

  do uri <- parseURI uriStr
     doc <- evenSimplerHTTP uri
     parsed <- parse grammar uriStr doc

Every time I hit an API boundary, I need to write a function to lift errors into
my monad.  And since these errors have disparate types (strings, ConnError,
ParseError), writing those lifting functions gets a little icky.

An ideal error-reporting convention would have several properties:

a) Provide a way to report "assertion failures" from any kind of code. These
errors never should have happened, but cropped up anyway, so they aren't worth
cluttering the API to think about.  The existing 'error' function serves this
purpose admirably.

b) Provide a way to say, "You know that thing you just asked for? It doesn't
exist" (e.g., Data.Map.lookup). The current convention of using Monad/fail is an
admirable solution, because it integrates into whatever error-reporting style
the caller is currently using.

c) Provide a unified way to deal with the error ADTs defined by libraries, e.g.,
ConnError, ParseError, etc. At the moment, this is pretty non-trivial: You need
to either smash everything down to a string, or use something hairy, such as
'(Error e, Typeable e) => Either e a'. This is where novice Haskell programmers
are most likely to wind up in trouble.

d) Provide a way to deal with errors in mixed functional/IO-based code. It would
be especially nice to have lifting functions that converted Either/ErrorT-based
errors into the exceptions used in the IO monad.

I think the current solutions for (a) and (b) are great, but (c) and (d) often
frustrate me.

>     * can we identify error handling strategies from the list that should
>     not be used anymore? (throwDyn?)

One point I made earlier about throwDyn: Out of the 8 error-handling strategies,
throwDyn is the only one that can mix ConnError and ParseError in a reasonably
seemless fashion. I'm not saying that programmers should use throwDyn; just that
it's the only approach which really handles (c) above. And even then, it only
works in the IO monad.

> can we make precise recommendations about which error strategies to use?

As an aspiring Haskell library author, I crave guidance. :-)

Thank you to everyone who's interested in this topic!

Cheers,
Eric





More information about the Libraries mailing list