🎓 A1Learner
🔍
0 of 50 lessons completed

Haskell Transformer Stacks

A transformer stack layers two or more effects in one type. The stack in this lesson reads a shared value and keeps a changeable count.

Peel ReaderT first, then StateT

The stack reads 5 and starts from 10. It returns the starting value 10 and leaves 15 behind.

import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Control.Monad.Trans.Class
type Stack a = ReaderT Int (StateT Int IO) a
action :: Stack Int
action = do
  n <- ask
  s <- lift get
  lift (put (s + n))
  return s
main :: IO ()
main = do
  (result, final) <- runStateT (runReaderT action 5) 10
  print result
  print final

The same peel works over a pure inner monad

The pure stack reads 4 and starts from 20. It returns 20 and leaves 24 behind.

import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Control.Monad.Trans.Class
import Data.Functor.Identity
type PureStack a = ReaderT Int (StateT Int Identity) a
action :: PureStack Int
action = do
  n <- ask
  s <- lift get
  lift (put (s + n))
  return s
main :: IO ()
main = print (runIdentity (runStateT (runReaderT action 4) 20))

The stack can combine both effects in one result

The stack reads 3 and starts from 40. It returns 46 and leaves 43 behind.

import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Control.Monad.Trans.Class
type Stack a = ReaderT Int (StateT Int IO) a
action :: Stack Int
action = do
  n <- ask
  s <- lift get
  lift (put (s + n))
  return (n * 2 + s)
main :: IO ()
main = do
  (result, final) <- runStateT (runReaderT action 3) 40
  print result
  print final

Example

import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Control.Monad.Trans.Class
type Stack a = ReaderT Int (StateT Int IO) a
bonusScore :: Stack Int
bonusScore = do
  bonus <- ask
  score <- lift get
  lift (put (score + bonus))
  return score
main :: IO ()
main = do
  (result, final) <- runStateT (runReaderT bonusScore 10) 50
  print result
  print final

Key points

  • Stack: a type that layers two or more effects in one program.
  • Alias: a short name for the full stack type, such as a ReaderT over StateT over IO type.
  • Peel the outer layer first with runReaderT, which leaves a StateT action.
  • Peel the next layer with runStateT and an initial state.
  • The final peel gives a pair holding the result and the final state.
  • Use lift to run the inner layer's state steps inside the outer layer.

In this example, the block asks for the bonus 10, reads the starting score 50 with a lifted get, and stores 60 with a lifted put. Peeling ReaderT first and StateT second gives the starting score 50 and the final state 60.

Note

the peel 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

In which order do you peel ReaderT over StateT over IO?