A monad transformer...
The resulting monad has both:
StateT s m
Notes
type MyMonad = StateT String IO
algorithm :: MyMonad Int
algorithm = do
oldState <- get
input <- lift getLine
put input
pure (length oldState)
runStateT :: StateT s m a -> s -> m (a, s)
Every monad transformer FooT has a runFooT function. For example for StateT:
In our example, we can use that to run the algorithm in the `main` function:
main :: IO ()
main = do
initialState <- getLine
(result, newState) <- runStateT algorithm initialState
print result
type MyMonad = StateT String (ReaderT Config IO)
We can repeat the process of applying a monad transformer to combine more than two effects:
StateT String |
ReaderT Config |
IO |
algorithm :: MyMonad Int
algorithm = do
input <- lift (lift getLine)
...
The Stack:
class MonadReader r m where
ask :: m r
The functionality of the different effects is actually defined in typeclasses, for example:
instance MonadReader r m => MonadReader r (StateT s m) where
ask = lift ask
Transformers typically implement these typeclasses if the base monad implements it:
type MyMonad = StateT String (ReaderT Config IO)
algorithm :: MyMonad Int
algorithm = do
cfg <- ask
...
So in practice:
How and why do transformers work?
Live coding