Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Result of monad inside monad transformer

This is my first acquaintance with Monad Transformers, so the answer might be obvious.

Let's say I am inside a do block of type StateT MyMonad MyType, I want to make another function of the same type both modify the state and return a value of type MyMonad MyType. How can I achieve that? I think the examples here show it in guessSession, yet I can't seem to understand how to apply it!

like image 714
aelguindy Avatar asked Jan 20 '12 15:01

aelguindy


1 Answers

If you want to use the underlying monad in a monad transformer, you can use lift:

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

In this case, t is StateT MyState, and m is MyMonad. So, for example:

foo :: StateT MyState MyMonad MyType
foo = do
  modify $ \s -> s+1
  lift $ doSomethingInMyMonad 42

Monad transformers aren't "layered on" in the sense that you'd return a value of type MyMonad MyType from inside; it's a more literal transformation: they turn a monad into a new one that has the ability to run actions in the transformed monad. So, you can think of StateT s m as just the regular State s monad, except that you can also use lift to run turn actions in m into actions in StateT s m.

If you're using the standard Monad Transformer Library (mtl) transformers like StateT, ReaderT, etc., you don't actually have to use lift; things like modify and ask work in any monad with the right transformer somewhere in the stack. (A stack is just a tower of transformed monads, like StateT s (ReaderT r IO).)

Additionally, if you have a large stack with IO at the bottom, there's a convenience function for lifting an IO action up any number of layers:

liftIO :: (MonadIO m) => IO a -> m a

So liftIO (putStrLn "Hello, world!") works in IO, StateT Int IO, ContT r (WriterT [String] IO), and so on.

(As an additional note, foo here isn't actually a function; a more accurate term is action or computation.)

like image 111
ehird Avatar answered Sep 28 '22 14:09

ehird