Reputation: 4795
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 Move
s 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 Move
s 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
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