haskellmonadsmonad-transformersmonadplus

Defining MonadPlus instance for a transformer on top of ErrorT


I would like define a monad transformer, that, among other things, endows a base monad with error functionality. The transformed monad should be an instance of MonadPlus if the base monad is, but I can't figure out how to define the MonadPlus instance so that the ErrorT transformer will return more than one answer (if there is more than one answer). My two attempts are in the code below. At the end of the code, is a statement showing how the monad transformer should behave if the base monad is []. Thanks.

import Control.Monad.Error
import Control.Monad.Trans.Class

data MyTrans m a = MyTrans {runMyTrans :: ErrorT String m a}

instance Monad m => Monad (MyTrans m) where
    return  = MyTrans . lift . return
    m >>= f = MyTrans $ do x <- runMyTrans m
                           runMyTrans (f x)

instance MonadTrans MyTrans where
    lift m = MyTrans $ lift m

instance MonadPlus m => MonadPlus (MyTrans m) where
    mzero = MyTrans $ lift mzero
    -- Attempt #1 (Only reveals the first element)
    m `mplus` n = MyTrans $ (runMyTrans m) `mplus` (runMyTrans n)
    -- Attempt #2 (Incomplete: see undefined statements)
--     m `mplus` n = MyTrans $
--                   lift $ do a <- runErrorT $ runMyTrans m
--                             b <- runErrorT $ runMyTrans n
--                             case a of
--                               Right r 1
--                               Left _ -> undefined
--                                   -> case b of
--                                        Left _ -> undefined
--                                        Right t -> return r `mplus` return t



type MyMonad = MyTrans []

x = return 1 :: MyMonad Int
y = MyTrans $ throwError "Error" :: MyMonad Int

z = x `mplus` y

main = do
  print $ (runErrorT $ runMyTrans z) -- should be [Right 1, Left "Error"] 

Solution

  • instance MonadPlus m => MonadPlus (MyTrans m a) where
        mzero = MyTrans (ErrorT mzero)
        MyTrans (ErrorT m) `mplus` MyTrans (ErrorT n) = MyTrans (ErrorT (m `mplus` n))
    

    Beware: I have only verified that this has the behavior you asked for, and not checked that it satisfies the MonadPlus laws. Chances are good that it violates some or all of them! Use at your own risk.