
On Mon, Jan 5, 2009 at 1:48 PM, Peter Robinson
Hello,
One thing that's been bothering me about MonadError monads is the non-portability of code that uses a custom Error type. Meaning, if I have libraries A and B that use different error types, I won't be able to write a function func:
func = (funcA >> funcB) `catchError` (\e -> ...)
funcA :: ErrorT MyErrorA m ()
funcB :: ErrorT MyErrorB m ()
So I'm wondering whether there's a reason not to introduce a type class hierarchy instead of custom error types to make the code more portable.
I think this worry is related to a world view of "large monads", which also proliferates claims like "monads are not appropriate for large programs", and is related to fat interfaces in OOP. I claim that, like objects, monads are appropriate for little pieces of computations, and monadic computations in different monads deserve to be composed just as much so as those in the same monad. The complex type-directed approach gives the feel of exceptions from mainstream languages, but will run into problems when eg. two computations both use ErrorT String, but you want to differentiate the errors. All that is necessary is a simple function: mapError :: (e -> e') -> ErrorT e a -> ErrorT e' a mapError f = ErrorT . liftM (left f) . runErrorT (Where "left" is from Control.Arrow, and is a "semantic editor") Then your example can become:: func = (mapError Left funcA >> mapError Right funcB) `catchError` (\e -> ...) Luke