Confusion over the State Monad code on “Learn you a Haskell”

后端 未结 4 744
南笙
南笙 2021-02-02 10:30

I am trying to get a grasp on Haskell using the online book Learn you a Haskell for great Good.

I have, to my knowledge, been able to understand Monads so far until I hi

4条回答
  •  执笔经年
    2021-02-02 11:17

    I'm total newbie to Haskell and I couldn't understand well the State Monad code in that book, too. But let me add my answer here to help someone in the future.

    Answers:

    • What are they trying to accomplish with State Monad ?

      Composing functions which handle stateful computation.
      e.g. push 3 >>= \_ -> push 5 >>= \_ -> pop

    • Why pop takes no parameters, while it is suggested function f takes 1 parameter ?

      pop takes no arguments because it is wrapped by State.
      unwapped function which type is s -> (a, s) takes one argument. the same goes for push.
      you can unwrapp with runState.

      runState pop :: Stack -> (Int, Stack)
      runState (push 3) :: Stack -> ((), Stack)
      

      if you mean the right-hand side of the >>= by the "function f", the f will be like \a -> pop or \a -> push 3, not just pop.


    Long Explanation:

    These 3 things helped me to understand State Monad and the Stack example a little more.

    • Consider the types of the arguments for bind operator(>>=)

      The definition of the bind operator in Monad typeclass is this

      (>>=) :: (Monad m) => m a -> (a -> m b) -> m b
      

      In the Stack example, m is State Stack.
      If we mentaly replace m with State Stack, the definition can be like this.

      (>>=) :: State Stack a -> (a -> State Stack b) -> State Stack b 

      Therefore, the type of left side argument for the bind operator will be State Stack a.
      And that of right side will be a -> State Stack b.

    • Translate do notation to bind operator

      Here is the example code using do notation in the book.

      stackManip :: State Stack Int  
      stackManip = do  
           push 3  
           pop  
           pop  
      

      it can be translated to the following code with bind operator.

      stackManip :: State Stack Int  
      stackManip = push 3 >>= \_ -> pop >>= \_ -> pop
      

      Now we can see what will be the right-hand side for the bind operator.
      Their types are a -> State Stack b.

      (\_ -> pop) :: a -> State Stack Int
      (\_ -> push 3) :: a -> State Stack ()
      


    • Recognize the difference between (State s) and (State h) in the instance declaration

      Here is the instance declaration for State in the book.

      instance Monad (State s) where  
          return x = State $ \s -> (x,s)  
          (State h) >>= f = State $ \s -> let (a, newState) = h s  
                                              (State g) = f a  
                                          in  g newState 
      

      Considering the types with the Stack example, the type of (State s) will be

      (State s) :: State Stack
      s :: Stack
      

      And the type of (State h) will be

      (State h) :: State Stack a
      h :: Stack -> (a, Stack)
      

      (State h) is the left-hand side argument of the bind operator and its type is State Stack a as described above.

      Then why h becomes Stack -> (a, Stack) ?
      It is the result of pattern matching against the State value constructor which is defined in the newtype wrapper. The same goes for the (State g).

      newtype State s a = State { runState :: s -> (a,s) }
      

      In general, type of h is s ->(a, s), representation of the stateful computation. Any of followings could be the h in the Stack example.

      runState pop :: Stack -> (Int, Stack)
      runState (push 3) :: Stack -> ((), Stack)
      runState stackManip :: Stack -> (Int, Stack)
      

      that's it.

提交回复
热议问题