There are too many error handling conventions used in library
daniel.yokomizo at gmail.com
Sun Mar 11 20:22:01 EDT 2007
On 3/11/07, Eric Kidd <emk.lists at gmail.com> wrote:
> 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
Couldn't we define (yet another) class to deal with these:
class Failure f where
try :: (Monad m) => f a -> m a
'try' would only depend on 'return' and 'fail'. The example would then become:
do uri <- try $ parseURI uriStr
doc <- try $ evenSimplerHTTP uri
parsed <- try $ parse grammar uriStr doc
We could define most of the instances in the same module and it would
be a matter of importing 'Control.Failure' for most libraries, or I'm
being particularly obtuse today?
> 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!
More information about the Libraries