使用Maybe with State Monad

时间:2013-11-20 02:10:33

标签: haskell monads monad-transformers state-monad

我尝试使用push / pop / peek操作在Haskell中实现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变换器,但我还没有真正理解它们,所以我正在寻求有关如何实现该功能的帮助。或者我是否需要以完全不同的方式执行此操作?感谢。

1 个答案:

答案 0 :(得分:2)

这是因为编译器无法构造无限类型; - )。

更有帮助的是,请考虑以下几行:

runState (pushQueue 2 >> popQueue >>= pushQueue) emptyQueue

popQueue的类型是什么? QueueState Int (Maybe Int)。 (想想m (Maybe Int)

>>= pushQueue的类型是什么? QueueState Int Int -> QueueState Int ()(想想m Int -> m ()

因此,类型检查器会尝试统一类型Maybe aa - 这些类型统一的唯一方法是a如果是无限类型的Maybe (Maybe (Maybe (Maybe ...

解决方案是执行某事来处理Nothing案例。例如,return ()或推送取决于Maybe。

runState (pushQueue 2 >> popQueue >>= maybe (return ()) pushQueue) emptyQueue