I'n looking at the State Monad's put
and get
:
ghci> :t get
get :: MonadState s m => m s
ghci> :t runState
runState :: State s a -> s -> (a, s)
ghci> runState get [1,2,3]
([1,2,3],[1,2,3])
In get
's type signature: MonadState s m => m s
, how does [1,2,3]
have a type of MonadState s m
? It's not clear to me what the types of s
and m
are.
Also, can you please say more as to how to use put
?
ghci> :t put put :: MonadState s m => s -> m ()
Overall, it seems that I don't understand what MonadState s m
is. Could you please explain with put
and get
examples?
MonadState s m
is a typeclass constraint, not a type. The signature:
get :: MonadState s m => m s
Says that for some monad m
storing some state of type s
, get
is an action in m
that returns a value of type s
. This is pretty abstract, so let’s make it more concrete with the less-overloaded version of State
from transformers
:
get :: State s s
put :: s -> State s ()
Now say we want to use State
to keep a simple counter. Let’s use execState
instead of runState
so we can just pay attention to the final value of the state. We can get
the value of the counter:
> execState get 0
0
We can set the value of the counter using put
:
> execState (put 1) 0
1
We can set the state multiple times:
> execState (do put 1; put 2) 0
2
And we can modify the state based on its current value:
> execState (do x <- get; put (x + 1)) 0
1
This combination of get
and put
is common enough to have its own name, modify
:
> execState (do modify (+ 1)) 0
1
> execState (do modify (+ 2); modify (* 5)) 0
10
MonadState
is the class of types that are monads with state. State
is an instance of that class:
instance MonadState s (State s) where
get = Control.Monad.Trans.State.get
put = Control.Monad.Trans.State.put
So are StateT
(the state monad transformer, which adds state to another monad) and various others. This overloading was introduced so that if you’re using a stack of monad transformers, you don’t need to explicitly lift
operations between different transformers. If you’re not doing that, you can use the simpler operations from transformers
.
Here’s another example of how to use State
to encapsulate a map of variables with Data.Map
:
import Control.Monad.Trans.State
import qualified Data.Map as M
action = do
modify (M.insert "x" 2) -- x = 2
modify (M.insert "y" 3) -- y = 3
x <- gets (M.! "x")
y <- gets (M.! "y")
modify (M.insert "z" (x + y)) -- z = x + y
modify (M.adjust (+ 2) "z") -- z += 2
gets (M.! "z") -- return z
main = do
let (result, vars) = execState action M.empty
putStr "Result: "
print result
putStr "Vars: "
print vars
In get's type signature: MonadState s m => m s, how does [1,2,3] have a type of MonadState s m? It's not clear to me what the types of s and m are.
ghci> runState get [1,2,3]
The function runState
takes two arguments: the first is the State
action to run, and the second is the initial state. So, since the initial state is [1,2,3]
which is a list of integers (*), the state type s
is just [Integer]
.
(*) Actually, [1,2,3] :: Num a => [a]
before GHCi defaults it, but for simplicity's sake let's use [Integer]
as GHCi does.
Hence, we see that runState
is specialized to
runState :: State [Integer] a -> [Integer] -> (a, [Integer])
Now, about the first argument:
get :: MonadState s m => m s
We must have m s = State s a
because we are passing it to runState
which requires such type. Hence:
runState :: State [Integer] a -> [Integer] -> (a, [Integer])
get :: MonadState s m => m s
with m s = State [Integer] a
The latter equation can be simplified as follows:
runState :: State [Integer] a -> [Integer] -> (a, [Integer])
get :: MonadState s m => m s
with m = State [Integer]
and s = a
Substituting s
:
runState :: State [Integer] a -> [Integer] -> (a, [Integer])
get :: MonadState a m => m a
with m = State [Integer]
Substituting m
:
runState :: State [Integer] a -> [Integer] -> (a, [Integer])
get :: MonadState a (State [Integer]) => State [Integer] a
Now, the constraint MonadState a (State [Integer])
is satisfied only when a = [Integer]
. This is tricky to see, since the MonasState
type class exploits a functional dependency to enforce that every monad in that class has only one related state type. This is also made more complex from State
being a wrapper around StateT
. Anyway, we get:
runState :: State [Integer] a -> [Integer] -> (a, [Integer])
get :: MonadState a (State [Integer]) => State [Integer] a
with a = [Integer]
So,
runState :: State [Integer] [Integer] -> [Integer] -> ([Integer], [Integer])
get :: MonadState [Integer] (State [Integer]) => State [Integer] [Integer]
And since the constraint is satisfied:
runState :: State [Integer] [Integer] -> [Integer] -> ([Integer], [Integer])
get :: State [Integer] [Integer]
And now we can see the involved ground types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With