Home > Software engineering >  Haskell's do syntax and its pattern matching behaviour
Haskell's do syntax and its pattern matching behaviour

Time:11-23

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.

CodePudding user response:

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
  • Related