2

我有一个小任务来模拟涉及状态的单子代码中的命令式循环,并且应该没有 IO,任务是在条件下退出循环,这是我的尝试:

> execState (forever $ modify (+1) >>= \x -> guard $ x < 5 ) 1

所以我希望在这里得到类似 4 的东西,而是得到这个神秘的信息:

<interactive>:1:43:
    No instance for (MonadPlus Data.Functor.Identity.Identity)
      arising from a use of `guard' at <interactive>:1:43-47
    Possible fix:
      add an instance declaration for
      (MonadPlus Data.Functor.Identity.Identity)
    In the first argument of `($)', namely `guard'
    In the expression: guard $ x < 5
    In the second argument of `(>>=)', namely `\ x -> guard $ x < 5'

没有警卫,整个事情都可以正常工作,但出于某种原因,它似乎完全讨厌警卫。

UPD:终于我让它运行起来了,感谢 hammar 的类型提示。尽管它什么都没有返回,但我知道它运行了 5 次,这很酷,不知道它现在有什么用处。

runStateT (forever $ do { modify (+1); x <- get; guard $ x < 5 } :: StateT Int Maybe Int) 1
4

2 回答 2

2

正如错误消息试图告诉您的那样,guard要求您使用的 monad 必须是类型 class 的实例MonadPlus

在此示例中,您使用的是Statemonad,它实际上是monadStateT顶部的转换器Identity。有一个MonadPlus实例StateT,但它要求底层的 monadMonadPlus也必须是 a,但事实Identity并非如此。

对于您的示例,您可以尝试类似

> let loop = modify (+1) >> get >>= \x -> if x < 5 then loop else return x in execState loop 1
于 2011-04-04T17:36:20.337 回答
1

不是所有的单子都支持守卫;只有那些在 MonadPlus。看起来您正在使用 Identity monad(可能作为 execState 的一部分)。当守卫失败时,您希望发生什么?

于 2011-04-04T17:32:21.897 回答