Search code examples
haskelltypesfunctional-programmingstatedo-notation

Haskell's do syntax and its pattern matching behaviour


I am currently trying to understand the do notation as taught in: http://learnyouahaskell.com/for-a-few-monads-more ...Here it is applied to simple functions and not a monad.

type Stack = [Int]

push :: Int -> Stack -> ((), Stack)
push a stack = ((),a:stack)

pop :: Stack -> (Int, Stack)
pop (x:xs) = (x, xs)

I dont understand the do-syntax fully, I am curious: Is this

turnipStack2 :: Stack -> (Int, Stack)
turnipStack = do
  push 3 
  c <- pop
  pop

.. the same as

turnipStack :: Stack -> (Int, Stack)
turnipStack = do
  push 3 
  c <- pop
  return c

If so, why is that. And could I have done the first mentioned without writing pop in the end. In that case I get an error although I don't understand it.


Solution

  • First, to actually get this working you need to change the signatures to use the state monad. Currently your do block refers to the (Stack->) monad, aka Reader Stack, but you want State Stack instead.

    import Control.Monad.Trans.State
    
    push :: Int -> State Stack ()
    push a = state $ \stack -> ((),a:stack)
    
    pop :: State Stack Int
    pop = state $ \(x:xs) -> (x, xs)
    
    turnipStack2 :: State Stack Int
    turnipStack2 = do
      push 3 
      c <- pop
      pop
    

    This is not equivalent to

     do
      push 3
      c <- pop
      return c
    

    The latter just pops off the one element you stored again, and then gives it back. This could actually be simplified to

     do
      push 3
      pop
    

    thanks to the monad law do{ x<-a; return x } ≡ a (right identity).

    By contrast, turnipStack2 first pops off the 3 you pushed, but doesn't use it (it's just discarded) then pops off another element which is the result value.

    Applying the right identity law in reverse, you could however write it thus:

     do
      push 3 
      _ <- pop
      d <- pop
      return d
    

    Binding an unused variable can be omitted, so

    turnipStack2 = do
      push 3 
      pop
      d <- pop
      return d