Reputation: 41939
Looking at the State Monad's wiki, I'm trying to understand the runState
and put
functions.
As I understand runState
, it takes a first argument of State
, which has a "universe", s
, and a value, a
. It takes a second argument of a universe. Finally, it returns a (a, s)
where a
is the new value and s
is the new universe?
ghci> :t runState
runState :: State s a -> s -> (a, s)
Example:
ghci> let s = return "X" :: State Int String
ghci> runState s 100
("X",100)
However, I'm don't understand the put
result:
ghci> runState (put 5) 1
((),5)
Since runState
returns an (a, s)
, why is the a
of type ()
?
I'm not confident on my above attempted explanations. Please correct me, and answer my question on put
.
Upvotes: 4
Views: 1618
Reputation: 27225
The runState
function takes an action in the State monad and an initial state and gives you back both the result of the calculation and the final state.
A typical use case for runState
is to hand it a complex action with an intial and to then get the final result and the state back. In your example, the action is a simple primitive, put
.
The put
action takes a new state and produces ()
(pronounced unit) for a value. It is very similar to how putStrLn has the type IO ()
It performs an action in the monad, but does not produce a useful value.
So, with runState (put 5) 1
the initial state, 1
gets blown away by the new state 5
. The result the state calculation is the result of put
that is ()
.
Just for kicks, lets look at something just a little more interesting:
runState (puts 5 >> return "hello world!") undefined
--> ("hello world!", 5)
Here we have two actions glued together with >>
(I read this as "then", it is a limited form of bind >>=
where we just drop the result from the left hand side). The first action changes the state to 5
the second doesn't touch the state, but results in the string "hello world!" which then becomes the value of the entire calculation.
Upvotes: 1
Reputation: 30237
The easiest way to really understand State
, IMHO, is just to study the code and understand it well enough that you can implement it from memory, like I'm about to do:
import Control.Applicative
newtype State s a = State { runState :: s -> (a, s) }
instance Functor (State s) where
fmap f fa = State $ \s -> f (runState fa s)
instance Applicative (State s) where
pure a = State $ \s -> (a, s)
ff <*> fa = State $ \ s ->
let (f, s') = runState ff s
(a, s'') = runState fa s'
in (f a, s'')
instance Monad (State s) where
return = pure
ma >>= f = State $ \s ->
let (a, s') = runState ma s
(b, s'') = runState (f a) s'
in (b, s'')
get :: State s s
get = State $ \s -> (s, s)
put :: s -> State s ()
put s = State $ \_ -> ((), s)
modify :: (s -> s) -> State s ()
modify f = State $ \s -> ((), f s)
Since
runState
returns an(a, s)
, why is the a of type()
?
That really is just arbitrary/conventional. Taking the above as a baseline, we could just as well write modify
, get
and put
like this:
-- | Replaces the state with the result of applying it to the supplied
-- function. The result of the action is the original state.
modify :: (s -> s) -> State s s
modify f = State $ \s -> (s, f s)
-- `get` is just what you get when you `modify` with an identity function.
get :: State s s
get = modify (\s -> s)
-- This version of `put` differs from the canonical one because it returns
-- the old value.
put :: s -> State s s
put s = modify (\_ -> s)
In this version modify
and put
have the same effects as the original, but additional produce the old state as their result. Clients that use modify
and put
only for the effect would not generally notice the difference.
Alternatively, the "return old state" versions of modify
and put
can be written in terms of the official ones. For example:
swap :: s -> State s s
swap s = do
orig <- get
put s
return orig
So most of these operations are interdefinable, it doesn't much matter which ones are "basic" and which ones not...
Upvotes: 1
Reputation:
When using put
with the State
monad, it has the type s -> State s ()
.
put
sets the state to its argument, and that's all it does. As for its return value: it's essentially a dummy value, because there's nothing useful to return.
This is also evident in its definition put s = state $ \ _ -> ((), s)
.
Upvotes: 6