Фундаментальные трансформеры монад ReaderT (read-only контекст: например, настройки программы из конфигурационного файла), WriterT (write-only контекст: например, лог работы программы) и StateT (read/write контекст: например, счётчики и прочее внутреннее состояние логики программы) стандартных библиотек mtl и transformers очень употребительны. Их можно композиционно параметризовать любой “объемлющей” монадой для моделирования требуемого вычислительного контекста (например, WriterT w IO для программы, которая общается по сети и протоколирует (логирует) свою работу моноидом r). Трансформеры легко параметризуются друг-другом; мы работает с вложенными в “стопку” монадами путём лифтинга комбинаторов к нужным монадам с помощью функций семейства lift.

Кстати, типы Writer, Reader и State (без T на конце) из популярных книг по Haskell для новичков - суть параметризованные монадой Data.Functor.Identity трансформеры.

Что может быть лучше этих трёх монад? Правильно, - одна монада, которая сочетает в себе сразу все три!

Тип RWST и RWS (по аналогии с другими XxxT) и соответствующие комбинаторы содержатся в модуле Control.Monad.RWS библиотеки трансформеров.

type RWS r w s = RWST r w s Identity

Тип RWST “из коробки” реализует все три интерфейса вышеуказанных стандартных монад:

(Monad m, Monoid w) => MonadReader r (RWST r w s m)
(Monad m, Monoid w) => MonadState s (RWST r w s m)
(Monoid w, Monad m) => MonadWriter w (RWST r w s m)

А значит мы можем просто забить на лифт между трансформерами и смешивать известные комбинаторы ask (Reader), tell (Writer), modify (State) и т.п. как нам заблагорассудится:

m :: Monoid w => RWST r w s IO String
m = do
  x <- ask
  tell mempty
  modify (\s -> s)
  str <- liftIO getLine

  return str

Запуск (т.е. “разворачиваение”) монады RWST происходит аналогично другим трансформерам функциями runRWST, evalRWST и execRWST. Начальное read-only окружение r и стэйт s задаются при запуске.