tonicebrian
tonicebrian

Reputation: 4795

What is the pattern to combine 2 MonadState instances into the same monad?

Say that I'm implementing a TicTacToe game server. In order to better test it I define a class for the effect of getting a move from a player:

class (Monad m, MonadState TTTState m) => TTTMonad m where 
  getMove :: m Move

In "production" the getMove will be an IO action waiting for input from the user, but when testing this I want to provide a list of pre canned Moves to replay a particular game.

So my initial approach was to create a tuple with the moves to be replayed and the TTTState itself:

newtype ReplayMonad a = ReplayMonad { unReplay :: State ([Move], TTTState) a }

but then I cannot have MonadState [Move] and MonadState TTTState for the same monad instance because of the functional dependency. I want this double dependency because I wanted to do:

instance TTTMonad ReplayMonad where
  -- Consume the first move in the list and provide it as a result of the context
  getMove = do 
    m:ms <- gets fst
    modify (\(_, gs) -> (ms, gs)) -- Have a new state with the remaining moves
    return m

All my code is only parameterized by MonadState TTTState and I just wanted to introduce the extra piece of state because I need to retrive Moves from within the context of the monad.

What am I doing wrong? What is the usual pattern to deal with this kind of extensions to a simpler monad?

Upvotes: 2

Views: 84

Answers (1)

Li-yao Xia
Li-yao Xia

Reputation: 33429

You don't need two MonadState instances. You only need MonadState TTTState since it is a superclass of TTTMonad, but instead of MonadState ([Move], TTTState) you can either have specialized variants of get and put for ReplayMonad

getMoves :: ReplayMonad [Move]
putMoves :: [Move] -> ReplayMonad ()

or use the ReplayMonad constructor, since the underlying monad exactly gives you access to the whole state.

   getMove :: ReplayMonad [Move]
   getMove = ReplayMonad $ do
     (m : ms, gs) <- get
     put (ms, gs)
     return m

Upvotes: 0

Related Questions