Prev: Standard monad transformers TOC: Contents Next: More examples with monad transformers

Anatomy of a monad transformer


In this section, we will take a detailed look at the implementation of one of the more interesting transformers in the standard library, StateT. Studying this transformer will build insight into the transformer mechanism that you can call upon when using monad transformers in your code. You might want to review the section on the State monad before continuing.

Combined monad definition

Just as the State monad was built upon the definition

newtype State s a = State { runState :: (s -> (a,s)) }
the StateT transformer is built upon the definition
newtype StateT s m a = StateT { runStateT :: (s -> m (a,s)) }
State s is an instance of both the Monad class and the MonadState s class, so StateT s m should also be members of the Monad and MonadState s classes. Furthermore, if m is an instance of MonadPlus, StateT s m should also be a member of MonadPlus.

To define StateT s m as a Monad instance:
newtype StateT s m a = StateT { runStateT :: (s -> m (a,s)) }

instance (Monad m) => Monad (StateT s m) where
    return a          = StateT $ \s -> return (a,s)
    (StateT x) >>= f  = StateT $ \s -> do (v,s')      <- x s            -- get new value, state
                                          (StateT x') <- return $ f v   -- apply bound function to get new state transformation fn
                                          x' s'                         -- apply the state transformation fn to the new state
Compare this to the definition for State s. Our definition of return makes use of the return function of the inner monad, and the binding operator uses a do-block to perform a computation in the inner monad.

We also want to declare all combined monads that use the StateT transformer to be instaces of the MonadState class, so we will have to give definitions for get and put:
instance (Monad m) => MonadState s (StateT s m) where
    get   = StateT $ \s -> return (s,s)
    put s = StateT $ \_ -> return ((),s)

Finally, we want to declare all combined monads in which StateT is used with an instance of MonadPlus to be instances of MonadPlus:
instance (MonadPlus m) => MonadPlus (StateT s m) where
    mzero = StateT $ \s -> mzero
    (StateT x1) `mplus` (StateT x2) = StateT $ \s -> (x1 s) `mplus` (x2 s)

Defining the lifting function

The final step to make our monad transformer fully integrated with Haskell's monad classes is to make StateT s an instance of the MonadTrans class by providing a lift function:
instance MonadTrans (StateT s) where
    lift c = StateT $ \s -> c >>= (\x -> return (x,s))
The lift function creates a StateT state transformation function that binds the computation in the inner monad to a function that packages the result with the input state. The result is that a function that returns a list (i.e., a computation in the List monad) can be lifted into StateT s [], where it becomes a function that returns a StateT (s -> [(a,s)]). That is, the lifted computation produces multiple (value,state) pairs from its input state. The effect of this is to "fork" the computation in StateT, creating a different branch of the computation for each value in the list returned by the lifted function. Of course, applying StateT to a different monad will produce different semantics for the lift function.

Functors

We have examined the implementation of one monad transformer above, and it was stated earlier that there was no magic formula to produce transformer versions of monads. Each transformer's implementation will depend on the nature of the computational effects it is adding to the inner monad.

Despite this, there is some theoretical foundation to the theory of monad transformers. Certain transformers can be grouped according to how they use the inner monad, and the transformers within each group can be derived using monadic functions and functors. Functors, roughly, are types which support a mapping operation fmap :: (a->b) -> f a -> f b. To learn more about it, check out Mark Jones' influential paper that inspired the Haskell monad template library.


Prev: Standard monad transformers TOC: Contents Next: More examples with monad transformers