简体   繁体   English

使用Maybe with State Monad

[英]Using Maybe with State Monad

I'm trying to implement a FIFO Queue in Haskell with push/pop/peek operations, and this is what I got so far. 我正在尝试使用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 [] []

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. 如你所见,我将一个返回类型包含在一个Maybe中,这样如果我弹出一个空队列或者查看一个空队列,我就得到一个Nothing,否则就是Just元素。

So I also thought I could use the State monad to easily chain operations. 所以我也认为我可以使用State monad轻松连接操作。 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. 然后回来(Just 3,Queue {inbox = [1], outbox = []}) ,这就是我想要的。 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 . 也就是说,使用>>=来自pop的链接应该能够进入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. 我想我可能需要使用StateT转换器,但我还没有真正理解它们,所以我正在寻找有关如何实现该功能的帮助。 Or do I need to do this in a completely different way? 或者我是否需要以完全不同的方式执行此操作? Thanks. 谢谢。

It is because the compiler can not construct an infinite type ;-). 这是因为编译器无法构造无限类型;-)。

More helpfully, consider the line: 更有帮助的是,考虑一下:

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

What is the type of popQueue ? popQueue的类型是popQueue QueueState Int (Maybe Int) . QueueState Int (Maybe Int) (think m (Maybe Int) ) (想想m (Maybe Int)

What is the type of >>= pushQueue ? 什么是>>= pushQueue的类型? QueueState Int Int -> QueueState Int () (think m Int -> m () ) QueueState Int Int -> QueueState Int () (想想m Int -> m ()

So the typechecker tries to unify the types Maybe a and a - the only way those types unify is if a if an infinite type of Maybe (Maybe (Maybe (Maybe ... . 所以typechecker试图统一类型Maybe aa -这些类型的统一的唯一途径是,如果a如果无限类型的Maybe (Maybe (Maybe (Maybe ...

The solution is to do something to handle the Nothing case. 解决方案是做一些事来处理Nothing案例。 For example, return () or push depending on the Maybe. 例如, return ()或push取决于Maybe。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM