Шаблон проектирования Haskell ReaderT против шаблона MTL StateT

Я разрабатываю небольшую игру, в которой в основном используется StateT, и просто обновляю состояние. Ниже приведена упрощенная версия:

{-# LANGUAGE TemplateHaskell #-}

import           Control.Lens
import           Control.Monad
import           Control.Monad.IO.Class
import           Control.Monad.State
import           Control.Monad.State.Class
import           System.Random

data PlayerState = PlayerState {
  _psName  :: String,
  _psScore :: Int
                               } deriving (Show)

makeClassy ''PlayerState

data Game = Game {
  _turns   :: Int,
  _players :: [PlayerState]
                 } deriving (Show)

makeClassy ''Game

randomGameInit :: IO Game
randomGameInit = do
  players <- replicateM 5 $  PlayerState <$> (replicateM 4 $ randomRIO ('a', 'z')) <*> randomRIO (1,10)
  return $ Game 0 players

update :: (MonadState s m, HasGame s) => m ()
update = do
  players . ix 0 . psName %= (\_ -> "mordor")
  turns %= (+1)
  exitCondition <- fmap (>10) (turns <%= id)
  unless exitCondition update

main :: IO ()
main = do
  init <- randomGameInit
  runStateT update init >> print "Game Over"

Недавно я узнал о шаблоне проектирования ReaderT vs mtl StateT, который поощряет замену StateT изменяемой ссылкой внутри ReaderT поверх IO.

Интересно, как мне адаптировать код с помощью ReaderT. В частности, многие функции Lens имеют типы: (MonadState sm) которые, очевидно, должны находиться внутри State. Означает ли это, что функции библиотеки Lens предназначены для StateT, а не для ReaderT? Как использовать Lens с ReaderT?

Ответ 1

Из того, что я видел, пользователи шаблонов ReaderT обычно не используют операторы объективов MonadState. Вместо этого используйте view для доступа к желаемому MVar (или любому другому изменяемому var, с которым вы имеете дело) и обновите его как обычно (например, с помощью modifyMVar).

Однако монада RIO предлагает соответствующий экземпляр MonadState. Возможно, лучший ответ, чем мой, может довольно легко адаптировать ваш код к монаде RIO.