Pop and push is rather straightforward:
push :: Int -> Stack ()
push x = T { run = \st -> ((), x : st) }
pop :: Stack (Maybe Int)
pop = T
{ run = \st -> case st of
(x : xs) -> (Just x, xs)
_ -> (Nothing, st)
}
pop
is type of Maybe Int
because the stack can be empty. In that case we just return Nothing
.
But what can we do with it? Well, not much without a Monad
instance. Let's do one. We need Functor
and Applicative
instances first:
instance Functor (Trans st) where
fmap f (T r) = T
{ run = \st ->
let (result, state) = r st
in (f result, state)
}
instance Applicative (Trans st) where
pure a = T { run = \st -> (a, st) }
(<*>) (T fr) (T r) = T
{ run = \st ->
let (result, state) = r st
(f, nextState) = fr state
in (f result, nextState)
}
instance Monad (Trans a) where
(>>=) (T r) f = T
{ run = \st ->
let (result, state) = r st
in run (f result) state
}
What does it give us? We can finally use our pop
and push
functions:
simpleStack :: Stack Int
simpleStack = do
push 10
push 20
push 30
Just x <- pop
return x
And we can test it this way:
main :: IO ()
main = putStrLn $ show $ fst $ run simpleStack []
Stack
? I feel like it should betype Stack a = Trans [a] a
. – Robin Zigmondstate -> (a, state)
, and that you'll then learn about the State monad and how it can simplify series of such stack operations. You don't need to know anything about how the monad "works" in order to write these basic functions. – Robin Zigmonda
is the return value of an action on aStack
, so for examplepush :: Int -> Stack ()
andpop :: Stack Int
. – chepner