Haskell MTL Stack
An MTL stack is a type of layered monads that provides several class operations at once. Each layer adds one capability, and the layers are peeled in order to run the stack.
A stack reads and changes state
Ask reads the environment 10, and modify adds it to the state. The value and the final state are both 10.
import Control.Monad.Reader
import Control.Monad.State
prog :: ReaderT Int (StateT Int IO) Int
prog = do
e <- ask
modify (+ e)
get
main :: IO ()
main = do
result <- runStateT (runReaderT prog 10) 0
print resultLocal works inside a stack
Local changes the environment for one ask inside the stack. The state still records only the outer environment.
import Control.Monad.Reader
import Control.Monad.State
prog :: ReaderT Int (StateT Int IO) (Int, Int, Int)
prog = do
a <- ask
b <- local (+ 5) ask
modify (+ a)
s <- get
return (a, b, s)
main :: IO ()
main = do
result <- runStateT (runReaderT prog 10) 0
print resultLiftIO runs plain IO in a stack
LiftIO brings a plain IO value into the stack. Adding it to the environment gives 17.
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.IO.Class
prog :: ReaderT Int (StateT Int IO) Int
prog = do
e <- ask
x <- liftIO (return 7)
return (e + x)
main :: IO ()
main = do
result <- runStateT (runReaderT prog 10) 0
print resultExample
import Control.Monad.Reader
import Control.Monad.State
prog :: ReaderT Int (StateT Int IO) (Int, Int)
prog = do
bonus <- ask
modify (+ bonus)
modify (* 2)
total <- get
bumped <- local (+ 3) ask
return (total, bumped)
main :: IO ()
main = do
result <- runStateT (runReaderT prog 5) 10
print resultKey points
- Layers: ReaderT adds an environment layer and StateT adds a state layer.
- Order: runReaderT peels the environment layer and runStateT peels the state layer.
- Ask in a stack: ask still reads the environment inside a larger stack.
- Modify in a stack: modify still changes the state inside a larger stack.
- LiftIO: liftIO runs a plain IO action inside a stack built on IO.
In this example, ask gives the bonus 5, modify adds it and doubles the state to 30, and local adds 3 to the environment for the final ask to give 8.
Note
the run order must match the layer order, or the types do not fit.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Haskell Playground
Exercise
For ReaderT Int (StateT Int IO), which runner peels the environment layer first?