Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell monads and a fail that doesn't require a string

I have the following monad transformer for dealing with errors in Haskell.

instance (Monad m, Error e) => Monad (EitherT e m) where
    return = EitherT . return . return
    m >>= k  = EitherT $ do
            a <- runEitherT m
            case a of
                Left  l -> return (Left l)
                Right r -> runEitherT (k r)
    fail = EitherT . return . Left . strMsg

It works fairly well, as I can instantiate Error with a custom class and have a pretty flexible means by which to handle errors.

fail is a bit silly, though, because it is type String -> EitherT e m, and the String restriction can be an annoying way to create errors. I end up with a whole lot of:

instance Error BazError where
    strMsg "foo" = FooError -- oh look we have no error context
    strMsg "bar" = BarError -- isn't that nice

What I'd like to do is create a new function, like fail, that is of type a -> e so that I can remove the (Error e) restriction. fail is especially convenient when the monad stack gets large, like when I end up with

EitherT BazError (StateT [BazWarning] IO) Foo

Is there a way to create a function that has the same behavior as fail with a less restrictive type? Or is fail implemented using deep haskell dark magic?

like image 400
So8res Avatar asked Dec 22 '11 09:12

So8res


2 Answers

Well, fail is called if you have a pattern-match failure in a do block, like if you have Just x <- something and something's result is Nothing. Apart from that, fail is an ordinary function.

For the problem with strMsg "foo" = FooError etc, does throwError offer a nicer interface for your use case?

like image 98
Daniel Fischer Avatar answered Oct 07 '22 09:10

Daniel Fischer


This article may be useful: http://blog.ezyang.com/2011/08/8-ways-to-report-errors-in-haskell-revisited/

Your EitherT is already in the standard library and called ErrorT. See the documentation: http://hackage.haskell.org/packages/archive/mtl/latest/doc/html/Control-Monad-Error.html#t:ErrorT

fail is a historical curiosity now, and may considered a design flaw, along with lack of Functor a => Monad a constraint. It is only a controversial feature to handle failed pattern matches in do notation.

throwError :: MonadError e m => e -> m a

is the most common replacement for fail, but more are available (see the article).

like image 25
nponeccop Avatar answered Oct 07 '22 09:10

nponeccop