How do I add lists or ListTs to this monad transformer?

I have a game record that represents the current state of the game.

data Game = Game { score :: Int, turn :: Int }

      

I want to be able to create a bunch of functions to change the state of the game and also use a random number generator and also keep a log of what happened in order to go from one state to another. Therefore, I created an entry GameState

containing additional information.

type History = [String]
data GameState = GameState Game StdGen History

      

Now I want to create a datatype for functions that will act on this GameState

. These will be modeled after game updates as well as rolling dice and logging. So I created a monad transformer for all the effects I want.

type Effect = WriterT History (RandT StdGen (State Game))

      

Writing a function to run Effect

in a given is GameState

pretty straightforward.

runEffect :: GameState -> Effect () -> GameState
runEffect (GameState game stdGen history) effect =
  let ((((), newHist), newGen), newGame) =
        runState (runRandT (runWriterT effect) stdGen) game
  in GameState newGame newGen newHist

      

Perfect. Now I want to create one more thing. Some Effects

may have several different results GameStates

. Therefore, mine runEffect

must really return [GameState]

. I need to add ListT

to this monad transformer maybe. And then all mine Effects

will have the ability to produce more than one result, if need be. But also, if they are just a one-to-one mapping, then they can act the same way too.

I tried to make the following changes:

type Effect2 = ListT (WriterT [String] (RandT StdGen (State Game)))

runEffect2 :: GameState -> Effect2 a -> [GameState]
runEffect2 (GameState game stdGen history) effect =
  let l = runListT effect
      result = map (\e->runState (runRandT (runWriterT e) stdGen) game) l
  in map (\((((), newHist), newGen), newGame)->
           GameState newGame newGen newHist)
         result

      

What I am trying to do is add ListT

to the transformer outside Writer

and Random

and State

because I want different computation branches to have different histories and independent states and random generators. But it doesn't work. I am getting the following type error.

Prelude Ξ»: :reload [1 of 1] Compiling Main             ( redux.hs, interpreted )

redux.hs:31:73: error:
    β€’ Couldn't match expected type β€˜[WriterT
                                       w
                                       (RandT StdGen (StateT Game Data.Functor.Identity.Identity))
                                       a1]’
                  with actual type β€˜WriterT [String] (RandT StdGen (State Game)) [a]’
    β€’ In the second argument of β€˜map’, namely β€˜l’
      In the expression:
        map (\ e -> runState (runRandT (runWriterT e) stdGen) game) l
      In an equation for β€˜result’:
          result
            = map (\ e -> runState (runRandT (runWriterT e) stdGen) game) l
    β€’ Relevant bindings include
        result :: [(((a1, w), StdGen), Game)] (bound at redux.hs:31:7)
        l :: WriterT [String] (RandT StdGen (State Game)) [a]
          (bound at redux.hs:30:7)
        effect :: Effect2 a (bound at redux.hs:29:44)
        runEffect2 :: GameState -> Effect2 a -> [GameState]
          (bound at redux.hs:29:1) 
Failed, modules loaded: none.

      

Does anyone know what I am doing wrong? I really want to be able to expand one GameState

by several GameStates

. Each of them has independent StdGen

and History

for this branch. I did this by putting everything in a post Game

and just using non-monodic functions for the effects. It works, and it's pretty straight forward. However, the composition of these functions is really annoying because they act like a state and I have to deal with it myself. This is what monads are great, so I decided to reuse this here would be wise. Unfortunately, this aspect of the list really confused me.

+3


source to share


1 answer


First, the immediate cause of the error is that the type runListT

...

GHCi> :t runListT
runListT :: ListT m a -> m [a]

      

... but you use it as if it produced [m a]

, not m [a]

. In other words, it shouldn't be map

in the definition result

.

Second, in the monadic stack, the inner monads rule over the outer ones. For example, packing, StateT

c ListT

, results in a state computation based on the state of the garden, which leads to several results. We can see that by specializing in the type runListT

:

GHCi> :set -XTypeApplications
GHCi> :t runListT @(StateT _ _)
runListT @(StateT _ _) :: ListT (StateT t t1) a -> StateT t t1 [a]

      



Wrapping ListT

with help StateT

, on the other hand, gives us a calculation that produces multiple states as well as the results:

GHCi> :t runStateT @_ @(ListT _)
runStateT @_ @(ListT _)
  :: StateT t (ListT t1) a -> t -> ListT t1 (a, t)

      

Thus, you want to swap the transformers in your stack. If you want to have multiple effects for everything as you described, and you don't need it IO

as your base monad, you don't need it at all ListT

- just put the []

stack at the bottom.

Third, avoid transformers in a tangent note ListT

. It is known to be illegal and is deprecated in the latest version of transformers . An easy replacement for this is provided by list-t package . (If at some point further down the road you can use the streaming channel library, you may also find your own versionListT

helpful.)

+2


source







All Articles