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