【问题标题】:Using Maybe with State Monad将 Maybe 与 State Monad 一起使用
【发布时间】: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 转换器还不够熟悉,无法真正回答,但这听起来像是 StateTMaybeT 转换器的任务。 Kleisli 箭头也可能有用。

标签: haskell monads monad-transformers state-monad


【解决方案1】:

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

更有用的是,考虑以下行:

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 ()或者push就看Maybe了。

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

【讨论】:

  • 谢谢,发生检查的解释是有道理的。但这真的是我要做的吗?它看起来很“hacky”。我认为所有这些>>= 的重点是隐藏这些复杂性。有没有办法以某种方式“进入”m,并使用Maybe 的 monad 规则将m (Maybe a) 值应用于a -> m b 类型的函数,这样我的原始示例才有效?只是想好好学习单子:)
  • 不,monadic bind 不打算/不能提供从任意类型到所需输入类型的转换......无论类型多么微不足道或常见。
猜你喜欢
  • 2020-11-16
  • 1970-01-01
  • 2012-06-30
  • 2018-02-09
  • 2019-02-15
  • 1970-01-01
  • 1970-01-01
  • 2011-08-02
  • 1970-01-01
相关资源
最近更新 更多