【发布时间】:2013-11-20 02:10:32
【问题描述】:
我正在尝试在 Haskell 中使用 push/pop/peek 操作实现一个 FIFO 队列,这就是我目前所得到的。
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 [] []
所以上面的工作,我可以推/弹出/偷看我的队列。如您所见,我将返回类型封装在一个 Maybe 中,因此如果我弹出一个空队列或查看一个空队列,我会得到 Nothing,否则会得到 Just 元素。
所以我也认为我可以使用 State monad 轻松地链接操作。我是这样处理的:
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
好吧,这似乎行得通。我能做到:
runState (pushQueue 2 >> pushQueue 3 >> popQueue >> pushQueue 1 >> popQueue) emptyQueue
然后返回(Just 3,Queue {inbox = [1], outbox = []}),这正是我想要的。但我不能这样做:
runState (pushQueue 2 >> popQueue >>= pushQueue) emptyQueue
导致:
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) ()
现在我想我明白为什么会这样了,但我不知道如何让它做我想做的事。也就是说,使用来自pop 的>>= 的链接应该能够馈送到push。我想我可能需要使用 StateT 转换器,但我还不太了解它们,所以我正在寻求有关如何实现该功能的帮助。还是我需要以完全不同的方式做到这一点?谢谢。
【问题讨论】:
-
我对 monad 转换器还不够熟悉,无法真正回答,但这听起来像是
StateT或MaybeT转换器的任务。 Kleisli 箭头也可能有用。
标签: haskell monads monad-transformers state-monad