I'm trying to implement a FIFO Queue in Haskell with push/pop/peek operations, and this is what I got so far.
data Queue a = Queue {
inbox :: [a],
outbox :: [a]
} deriving (Eq, Show)
push :: a -> Queue a -> Queue a
push e (Queue inb out) = Queue (e:inb) out
pop :: Queue a -> (Maybe a, Queue a)
pop q =
case top of
Nothing -> (top, emptyQueue)
Just elem -> (Just elem, poppedQueue)
where
(top, q') = peek q
poppedQueue = Queue (inbox q') (tail $ outbox q')
peek :: Queue a -> (Maybe a, Queue a)
peek q@(Queue [] []) = (Nothing, emptyQueue)
peek q@(Queue inb []) = peek $ Queue [] (reverse inb)
peek q@(Queue _ outb) = (Just $ head outb, q)
emptyQueue = Queue [] []
So the above works, and I can push/pop/peek at my Queue. As you see, I wrapped up the return type in a Maybe, so that I get a Nothing if I pop an empty queue or peek at an empty queue, and a Just element otherwise.
So I also thought I could use the State monad to easily chain operations. I proceeded as follows:
type QueueState a = State (Queue a)
pushQueue :: a -> QueueState a ()
pushQueue e = state $ \q -> ((),push e q)
popQueue :: QueueState a (Maybe a)
popQueue = state $ \q -> pop q
Allright, so that seems to work. I can do:
runState (pushQueue 2 >> pushQueue 3 >> popQueue >> pushQueue 1 >> popQueue) emptyQueue
and get back (Just 3,Queue {inbox = [1], outbox = []})
, which is what I wanted. But I can't do something like:
runState (pushQueue 2 >> popQueue >>= pushQueue) emptyQueue
which results in an:
Occurs check: cannot construct the infinite type: a0 = Maybe a0
Expected type: Maybe a0
-> StateT (Queue a0) Data.Functor.Identity.Identity ()
Actual type: Maybe a0 -> QueueState (Maybe a0) ()
Now I think I understand why that this, but I can't figure out how to make it do what I want. That is, a chaining using >>=
from a pop
should be able to feed into a push
. I'm thinking I might need to use a StateT transformer, but I don't really understand them yet, so I'm looking for help on how do implement that functionality. Or do I need to do this in a completely different way? Thanks.
StateT
orMaybeT
transformer. Kleisli arrows might be useful too. – Jeremy List