
Donald Bruce Stewart
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