I am trying to take e.g. ExceptT a (StateT A M)
, for some concrete type A
and monad M
, and wrap them up into my new custom monads.
The normal pattern is to define a newtype for your complete transformer stack.
data A = A
data E = E
newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a }
If there are any pieces of the stack that add meaningful new capabilities on their own you'd also define newtypes for those pieces.
Then you use GeneralizedNewtypeDeriving
to get instances for all of the various monad classes.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- base
import Control.Applicative
import Control.Monad
-- transformers
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Control.Monad.Trans.State.Lazy
-- mtl classes
import Control.Monad.Cont.Class
import Control.Monad.Error.Class
import Control.Monad.Reader.Class
import Control.Monad.State.Class
import Control.Monad.Writer.Class
data A = A
data E = E
newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a }
deriving (Functor, Applicative, Monad, MonadIO, -- classes from base and transformers
{- Alternative, MonadPlus, -} -- if E is a monoid
MonadState A, MonadError E, -- classes from mtl that MyBranchT provides
MonadCont, MonadReader r, MonadWriter w) -- classes from mtl that might be available from m
You'll have to write the MonadTrans
instance by hand. lift
is always just lift
once for each of the transformers in the stack.
instance MonadTrans MyBranchT where
lift = MyBranchT . lift . lift
If there are any pieces of the stack that add meaningful new capabilities X
on their own you'd also define a new MonadX
class for those capabilities, write MonadX
instances for each of the monad transfomers (StateT
, ExceptT
, ContT
, etc) if possible, and derive the MonadX
instance for your transformer stack (MyBranchT
).
You'll also usually make a type synonym for MyBranchT Identity
and functions to runMyBranchT
and runMyBranch
import Data.Functor.Identity
type MyBranch a = MyBranchT Identity a
runMyBranchT :: MyBranchT m a -> A -> m (Either E a, A)
runMyBranchT mb s = runStateT (runExceptT (getMyBranchT mb)) s
runMyBranch :: MyBranch a -> A -> (Either E a, A)
runMyBranch mb s = runIdentity $ runMyBranchT mb s