Cleaning Monads - turning a monodal transformer into a new monad

I'm trying to take for example. ExceptT a (StateT AM) , for a specific type A and monad M , and wrap them in my new custom monads.

At first, I determined that StateT AM often appears in other contexts, and so I decided that it would be best to wrap this in the monad M1 , and then wrap ExceptT a M1 in M2 .

The required property is to make instances of M1 and M2 MonadState and class M (suppose it is called MyMonadClass ). Also, M2 must be an instance of MonadError .

First, I started with simple type synonyms:

 type MyState = StateT AM type MyBranch a = ExceptT a MyState 

then I thought that I would first draw the declarations of the instance (without implementing the instance), and exactly where I first got stuck. instance MonadState A (MyState) did not seem to have the correct syntax. I thought that I would need to create newtype MyState' a = StateT a M , and then type MyState = MyState A (do not use language extensions where it is not necessary).

However, as soon as I started converting synonyms into newtype declarations, I started to lose connection with StateT AM and ExceptT ... types.

 newtype MyState' sa = MyState' { runMyState :: s -> (s, a) } type MyState = MyState' A newtype MyBranch ea = MyBranch { runMyBranch :: MyState (Either ea) } 

Now the transformers that have already been implemented have disappeared, and I think I'm trying to do something that doesn't make much sense. Therefore, my question is: how to properly transfer this behavior to the new composite monads that make the layers beneath them available to avoid unnecessary lifting and to keep things clear and well organized.

+8
haskell state-monad lifting monad-transformers newtype
source share
1 answer

The usual pattern is to define a new type for your complete transformer package.

 data A = A data E = E newtype MyBranchT ma = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a } 

If there are any parts of the stack that themselves add meaningful new features, you also define new types for these parts.

Then you use GeneralizedNewtypeDeriving to get instances for all the different 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 ma = 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 will need to write a MonadTrans instance manually. lift always just lift once for each of the transformers in the stack.

 instance MonadTrans MyBranchT where lift = MyBranchT . lift . lift 

If there are any parts of the stack that themselves add significant new features to X , you also define a new MonadX class for these features, write MonadX instances for each of the monad transforms ( StateT , ExceptT , ContT , etc.) and get a MonadX instance for your transformer stack ( MyBranchT ).

You also usually use a synonym like MyBranchT Identity and function in runMyBranchT and runMyBranch

 import Data.Functor.Identity type MyBranch a = MyBranchT Identity a runMyBranchT :: MyBranchT ma -> 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 
+9
source share

All Articles