Search code examples
haskellmonadsstate-monad

Binding computations with the State monad?


I'm looking to pass a State monad through the following functions:

e1 :: Int -> (Bool, Int)
e1 el 
  | el > 100           = (True, el)
  | otherwise          = (False, 0)

e2 :: Int -> (Bool, Int)
e2 el 
  | el > 200           = (True, el)
  | otherwise          = (False, 0)

e3 :: Int -> (Bool, Int)
e3 el 
  | el > 300           = (True, el)
  | otherwise          == (False, 0)

implementor :: State Bool Int
implementor = state e1 ...

main = do 
  print $ runState implementor 10 

Currently runState is passed an State s a (implementor) and a value (10), and then returning the tuple from e1.

However I'd like to bind these operations together, such as:

state e1 >>= e2 >>= e3  

e1 would pass its State Bool Int to e2, which would operate on the Int (through el), then pass it's resulting State Bool Int to e3 which, again, would operate on the Int in this incoming State.

I've found the instance of Monad State very confusing, following this guide:

instance Monad (State s) where
   return :: state $ \s -> (s, a)--this is returning a State which contains function (s -> (s, a))
   m >>= k = state $ \s -> let (a, s') = runState m s --? 
                     in runState (k a) s' 

I don't understand what this instance of bind is doing and how to use this to bind e1, e2 and e3 together?


Solution

  • If you use state :: (s -> (a, s)) -> State s a on e1, you end up with a State Int Bool:

    state e1 :: State Int Bool
    

    That is something that acts over a state (in this case an Int) and yields a Bool as a result of using that state. So if we want to use e1, e2 and e3 after each other in a stateful computation, we could use them with do-notation:

    allThree :: State Int ()
    allThree = do
      firstBool  <- state e1
      secondBool <- state e2
      thirdBool  <- state e3
      return thirdBool
    

    However, if we ignore the first two Bools, we can just remove the bindings:

    allThree :: State Int Bool
    allThree = do
      state e1
      state e2
      state e3
    

    And now we can rewrite do-notation with >> and >>=. We end up with

    allThree :: State Int Bool
    allThree = state e1 >> state e2 >> state e3
    

    As for how this works, let's have a look at >>=

      m >>= k = state $ \s -> let (a, s') = runState m s 
                              in runState (k a) 
    

    And m >> k is m >>= const k. So let's check what state e1 >> state 2 does:

     state e1 >> state e2 
       = state e1 >>= const (state e2)
       = state $ \s -> let (a, s') = runState (state e1) s in runState (const (state e2) a) s'
         -- simplify runState (state e1) s to e1 s
       = state $ \s -> let (a, s') = e1 s in runState (const (state e2) a) s'
         -- use "const"
       = state $ \s -> let (a, s') = e1 s in runState (state e2) s'
         -- again simplify runState (state e2) s' to e2 s'
       = state $ \s -> let (a, s') = e1 s in e2 s'
    

    Therefore, the following terms are the same:

    stateful  s = runState (state e1 >> state e2) s -- use above to show that
    stateless s = let (_, s') = e1 s 
                  in e2 s'
    

    Now, why was I able to use change runState (state f) to f? Because the definition of State is rather boring:

    -- simplified, modern versions use a more sophisticated approach!
    newtype State s a = State { runState :: s -> (a, s) }
    

    That is, a Stateful action is something that takes a state and returns a new one along to the value. The state function is thus rather simple:

    state :: (s -> (a, s)) -> State s a
    state = State
    

    And since runState (State f) is f, runState (state f) is also `f´.

    Therefore, we could write the Monad instance a little bit different:

    instance Monad (State s) where
      (State e1) >>= f = State e3
         where  
           e3 s = let (a, s')    = e s
                      (State e2) = f a
                  in e2 s'
    

    Remember, >>= expects a function that takes something and returns another action, whereas the >> can be used to chain actions after each other.