Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell ReaderT Design Pattern vs mtl StateT pattern

Tags:

haskell

I'm designing a small game which basically uses StateT and just updating the state. Below is the simplified version:

{-# 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"

I've recently learned about the ReaderT Design Pattern vs mtl StateT, which encourages replacing StateT with a mutable reference inside a ReaderT over IO.

I wonder how I should adapt the code using ReaderT. Most specifically, many Lens functions have types: (MonadState s m) which apparently need to be inside a State. Does this mean that Lens library functions are designed for StateT and not ReaderT? How to use Lens with ReaderT design pattern?

like image 266
McBear Holden Avatar asked Mar 11 '19 21:03

McBear Holden


1 Answers

From what I've seen, ReaderT pattern users typically don't use the MonadState lens operators. Instead, use view to access the desired MVar (or whatever kind of mutable var you're dealing with) and update that as usual (e.g. with modifyMVar).

The RIO monad offers an appropriate MonadState instance, though. A better answer than mine could probably adapt your code to the RIO monad fairly easily.

like image 61
Dan Burton Avatar answered Oct 17 '22 20:10

Dan Burton