Монада RWS: Reader + Writer + State в одном интерфейсе
Фундаментальные трансформеры монад 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 задаются при запуске.