A real-life example: If I'm in a good mood('good state'), when manager asks me about estimates, I give him a solid answer, but dares he to do that 3 times in a row, without some free snacks in between, my mood changes(I get to 'bad state') and the next 3 times he approaches I ask him not to disturb me with any of his nonsense.
Here's a log of my usual day:
[ Mood: Good, Patience: 3 ] -- 11:00 am, I'm happy
ESTIMATE -> "bla bla 6", [ Mood: Good, Patience: 2 ]
ESTIMATE -> "bla bla 1", [ Mood: Good, Patience: 1 ]
Cookies! -> "", [ Mood: Good, Patience: 3 again! ]
ESTIMATE -> "bla bla 7", [ Mood: Good, Patience: 2 ]
ESTIMATE -> "bla bla 2", [ Mood: Good, Patience: 1 ]
ESTIMATE -> "bla bla 9", [ Mood: BAD , Patience: -2 ] -- Enough!
ESTIMATE -> "Need a break!" [ Mood: BAD , Patience: -1 ]
ESTIMATE -> "Deploynig!", [ Mood: BAD , Patience: 0 ]
ESTIMATE -> "Lunch time!", [ Mood: Good, Patience: 3 ] -- Ok he needs me..
ESTIMATE -> "bla bla 6", [ Mood: Good, Patience: 2 ]
...
Now this model of me at work seems to fit the State
Monad.
newtype State s a = State { runState :: s -> (a, s) }
But how do I do this? The signature has room for a state, which in my case is (Mood,Patience)
, and not for input (ESTIMATE
or Cookies
). It's like I'm supposed to answer without even listening!
So my question is: How do I make not only Stateful but also Argumentful computation with State
monad of Haskell?
A stateful computation gets an input, a state and returns an output and a new state. So the type will be input -> state -> (state, output)
.
The runState
is just a partially applied stateful computation that has already taken its inputs.
Note also that when you compose stateful functions (i.e. when you use the >>=
bind operator or do
notation) you do exactly this: you supply the inputs as expression and the bind is responsible for passing around only the state.
You can call get
without using its return value, but then it gets lost. If you want to make use of it you have to use value <- get
and then provide the value
as an explicit input for the next stateful computation. The bind only comes into play when passing the state around.
Practical example: consider the function:
doStuff :: Int -> State Int Int
doStuff x = do
val <- get
put $ val+x+1
return 0
The doStuff
type has exactly the pattern input -> state -> (state, output)
. But the input
part is represented by the x
argument.
Once you provide x
you get something of type state -> (state, output)
which is exactly what runState
represents.
So you don't actually need the arguments for stateful actions because you can partially apply them beforehand to obtain "pure stateful computations that have no input" (those are scary quotes).
It sounds like what you're looking for is not State
but StateT
, a monad transformer that adds statefulness to an existing monad.
newtype StateT s m a = StateT (s -> m (a, s))
Given a state of type s
, a StateT s m a
action returns an m
action that, when run, produces a result and a new state. StateT s
is an instance of MonadTrans
:
instance MonadTrans (StateT s) where
--lift :: Monad m => m a -> StateT s m a
lift ma = StateT $
\s -> ma >>= \a -> pure (a, s)
Further, if m
is a Monad
, then so is StateT s m
.
So if you want to take "input" within some context (e.g., IO
), you are free to do so:
do
s <- get
input <- lift getLine
when (annoying input && annoyed s) $ put Angry
For IO
in particular, it's usually better to use liftIO
, which can lift through a whole stack of transformers.
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