Search code examples
haskellfunctional-programmingmonadsmonad-transformersstate-monad

Understanding the RWST in Haskell


I have looked at this to try to understand how several transformer monads interact, and especially getting a better understanding of lift and stacking with monads.

For the RWST monad found here (I think this is the best documentation of it), is it the case that it is a stacked monad where the Reader, Writer, State each are a monadic layer (and in that order of stacking). Or how is it supposed to be understood? When I look at the definition runRWST :: r -> s -> m (a, s, w) I understand it as taking reader-environment a state-environment and wrapping any monad m around the return value of RWS. This also means that there only exists two layers of monads in this monad. That is, the outer monad m , and a tuple containing several monads.

This in turn also means that you can only use the lift once. Lifting a value from either the reader or the state monad into the outer monad.

In that sense get and ask are just two functions applying one of either two of the inner monads. For this last point I am still not sure I understand why you would need both a reader a state-monad even having read this stackoverflow post. The reader is only meaningfull for read-only I guess, but if one didn't want that, could one use a transformer monad around two seperat state-monads?

An example:

The comments have given me reason to ponder and make the following more explicit.... Of the following type definition what what would be the inner monad, and the outer monad? Is the RWST itself a Monad wrapped around (and therefor an outer monad) Either String (the inner monad) ?

type MyRWST a = RWST
                 (String -> Either String MyType)       
                 [Int] 
                 (MyEnv, [String], [String])
                 (Either String)
                 a

Solution

  • The inner monad of a monad transformer is always a type parameter. The type you've provided isn't a transformer.

    type MyRWST a = RWST
                     (String -> Either String MyType)       
                     [Int] 
                     (MyEnv, [String], [String])
                     (Either String)
                     a
    

    This is a Monad. Compare this to a transformer like MaybeT.

    newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
    

    MaybeT takes two type parameters, and the first itself takes a parameter, so its kind is (* -> *) -> * -> *. With more explicit parentheses, that's (* -> *) -> (* -> *). And now we can see why it's called a transformer. It takes one monad (of kind * -> *) and transforms it into a new monad (also of kind * -> *).

    RWST is defined as

    newtype RWST r w s m a = RWST { unRWST :: r -> s -> w -> m (a, s, w) }
    

    Now this takes a lot of type arguments, but if we fix r, w, and s, we get a transformer. That is, RWST is not, itself, a monad transformer, but for any r, w, and s, RWST r w s is a transformer. The full kind of RWST is

    RWST :: * -> * -> * -> (* -> *) -> * -> *
    

    And while you can think of RWST as having three layers (reader, writer, and state), it really only does have one. The "next layer down" of RWST r w s m is really m. So to directly answer your question about lift, the type signature of lift is

    lift :: (MonadTrans t, Monad m) => m a -> t m a
    

    and when t ~ RWST r w s, we get

    lift :: Monad m => m a -> RWST r w s m a
    

    So a single lift takes us over the whole RWST r w s mess.