Getting Gamestate value in State Monad

all*_*all 1 haskell state-monad

I'm learning about state monads and i'm a little confused.

I have a data type

data GameState = GameState (Map String Double) Double Bool
deriving (Eq, Show)
Run Code Online (Sandbox Code Playgroud)

The second argument Double is a direction

and of course the state monad definition

newtype State s a = StateOf (s -> (s, a))
deState (StateOf stf) = stf
get = StateOf (\s0 -> (s0, s0))
put s = StateOf (\s0 -> (s , ()))
modify f = get >>= \s -> put (f s)
Run Code Online (Sandbox Code Playgroud)

So how do I write a function to get the direction

getDirection:: State GameState Double
Run Code Online (Sandbox Code Playgroud)

I've tried

getDirection = do
     x <- get
     return x
Run Code Online (Sandbox Code Playgroud)

But this will just return GameState, how do I get what the direction currently is?

And when I want to change the direction do I use put or modify?

bra*_*drn 5

Let’s start with what you have so far:

getDirection :: State GameState Double
getDirection = do
     x <- get
     return x
Run Code Online (Sandbox Code Playgroud)

(As an aside, this is really just the same as getDirection = get, since you’re simply running get and returning its return value.)

Firstly, what is the type of x here? Your state is of type GameState, and get just gets the state, so x :: GameState. So we can pattern match on it to get:

getDirection :: State GameState Double
getDirection = do
     (GameState map dir bool) <- get
     return (GameState map dir bool)
Run Code Online (Sandbox Code Playgroud)

At this point it should be obvious what to do: simply return dir instead of (GameState map dir bool).

And when I want to change the direction do I use put or modify?

You shouldn’t really ask two questions in the same post, but to answer this, let’s have a look at their types:

put    :: s        -> State s ()
modify :: (s -> s) -> State s ()
Run Code Online (Sandbox Code Playgroud)

The idea is that put simply writes a new state, whereas modify takes the existing state and modifies it using the given function. These functions are in fact equivalent in power, meaning you can replace either function with the other:

-- write ‘put’ using ‘modify’
put s = modify (\_oldState -> s)

-- write ‘modify’ using ‘put’ (and ‘get’)
modify f = do
    oldState <- get
    put $ f oldState
Run Code Online (Sandbox Code Playgroud)

However, usually it is easier to use put or modify in different situations. For instance, if you want to write a completely new state without reference to the old state, use put; if you want to take the existing state and change it a bit, use modify. In your case, you want to change the direction only, so it’s easiest to use modify so you can change the state with reference to what it used to be:

changeDirTo :: Double -> State GameState ()
changeDirTo newDir = modify (\(GameState map _ bool) -> GameState map newDir bool)

-- you can also do it using ‘put’, but it’s a bit harder and less elegant:
changeDirTo2 :: Direction -> State GameState ()
changeDirTo2 newDir = do
    (GameState map _ bool) <- get
    put $ GameState map newDir bool
Run Code Online (Sandbox Code Playgroud)

On the other hand, if you (say) wanted to assign a completely new GameState, put would be easier:

putNewGameState :: GameState -> State GameState ()
putNewGameState gs = put gs

-- the above is the same as:
-- putNewGameState = put

-- you can also do it using ‘modify’, but it’s a bit harder and less elegant:
putNewGameState2 :: GameState -> State GameState ()
putNewGameState2 gs = put (\_oldState -> gs)
Run Code Online (Sandbox Code Playgroud)