Haskell Monad Transformers
A monad transformer adds one effect to another monad. It layers the new effect on top of the monad underneath.
ReaderT adds a read-only value
ReaderT carries one shared value for the whole block. The block asks for 21 and doubles it.
import Control.Monad.Trans.Reader
import Data.Functor.Identity
main :: IO ()
main = print (runIdentity (runReaderT (do n <- ask; return (n * 2)) 21))StateT adds a changeable value
StateT carries a value that steps can replace. The block returns 41 and leaves 42 behind.
import Control.Monad.Trans.State
import Data.Functor.Identity
main :: IO ()
main = print (runIdentity (runStateT (do n <- get; put (n + 1); return n) 41))The shared value can be text
The shared value is not always a number. The block asks for a colour and returns it unchanged.
import Control.Monad.Trans.Reader
import Data.Functor.Identity
main :: IO ()
main = print (runIdentity (runReaderT ask "red"))Example
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Data.Functor.Identity
main :: IO ()
main = do
print (runIdentity (runReaderT (do n <- ask; return (n * 2)) 21))
print (runIdentity (runStateT (do n <- get; put (n + 1); return n) 41))Key points
- Transformer: a type that adds one effect to another monad.
- ReaderT: a transformer that adds a shared read-only value.
- StateT: a transformer that adds a changeable value.
- Use lift to run an action from the inner monad inside the outer layer.
- Run a transformer by peeling its layers in order.
In this example, the first block doubles the shared value 21 to 42, and the second block returns 41 while its state moves to 42. Together they show two different effects layered onto a plain result.
Note
transformers ship with GHC in the transformers library, so no extra install is needed.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Haskell Playground
Exercise
What does a monad transformer do?