MonadState
MonadState
is used to run state manipulating functions. Since only one type implements the class, we'll combine the class' definition and instance into one block:
newtype StateT state monad output =
StateT (\state -> monad (Tuple output state))
-- Pseudo syntax: combines class and instance into one block:
class (Monad m) <= MonadState s (StateT s m) where
state :: forall a. (s -> Tuple a s) -> StateT s m a
state f = StateT (\s -> pure $ f s)
Reading Its Do Notation
stateT_do_notation :: StateT State Value
stateT_do_notation = do
value1 <- state (\initialState -> Tuple value1 state2)
value2 <- state (\state2 -> Tuple value2 state3)
value3 <- state (\state3 -> Tuple value3 state4)
state (\state4 -> Tuple value4 state5)
Derived Functions
As we saw above, whenever we wrote state function
, function
always had to wrap our output into a Tuple
type:
(\state -> {- do stuff -} Tuple output nextState)
This gets tedious really fast. Fortunately, MonadState
's derived functions remove that boilerplate and emphasize the developer's intent:
get
: returns the stategets
: applies a function to the state and returns the result (useful for extracting some value out of the state)put
: overwrites the current state with the argumentmodify
: modify the state and return the updated statemodify_
: same asmodify
but returnunit
so we can ignore thebinding <-
syntax
sideBySideComparison :: State Int String
sideBySideComparison = do
state1 <- state (\s -> Tuple s s)
state2 <- get
shownI1 <- state (\s -> Tuple (show s) s)
shownI2 <- gets show
state (\s -> Tuple unit 5)
put 5
added1A <- state (\s -> let s' = s + 1 in Tuple s' s')
added1B <- modify (_ + 1)
state (\s -> Tuple unit (s + 1))
modify_ (_ + 1)
-- to satisfy the type requirements
-- in that the function ultimately returns a `String`
pure "string"
Returning to our previous example, crazyFunction
was implemented like so:
- Take some
initialState
value - Pass that value into
add1 :: State -> Tuple Int Int
, which returnsTuple value1 state2
- Pass
value
andstate2
intoaddValue1StringLengthTo :: Int -> Int -> Tuple String Int
wherevalue
will be converted into aString
, calledvalueAsString
- the length of
valueAsString
will be added tostate2
, which producesstate3
state3
is converted into aString
, calledvalue2
- the function returns
Tuple value2 state3
- Return
addStringLengthTo
's output:Tuple value2 nextState3
With MonadState
, we would now write:
crazyFunction :: State Int String
crazyFunction = do
value1 <- modify (_ + 1)
modify_ (_ + (length $ show value1))
gets show
main :: Effect Unit
main =
case (runState crazyFunction 0) of
Tuple theString theInt -> do
log $ "theString was: " <> theString -- "2"
log $ "theInt was: " <> show theInt -- 2
runState :: forall s a. StateT s Identity a -> s -> Tuple a s
runState stateT initialState =
let (Identity tuple) = runStateT stateT initialState
in tuple
runStateT :: forall s m a. StateT s m a -> s -> m Tuple a s
runStateT (StateT f) initialState = f initialState
Laws, Instances, and Miscellaneous Functions
For the laws, see MonadState's docs
For its instances, see:
To handle/modify the output of a state computation: