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