我有一个小任务来模拟涉及状态的monadic代码中的命令性循环,并且应该没有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
答案 0 :(得分:2)
正如错误消息试图告诉您的那样,guard
要求您使用的monad必须是类MonadPlus
类型的实例。
在此示例中,您使用的是State
monad,它实际上是StateT
monad之上的Identity
转换器。 MonadPlus
有StateT
个实例,但它要求基础monad也必须是MonadPlus
,Identity
不是。{/ p>
对于您的示例,您可以尝试类似
的内容> let loop = modify (+1) >> get >>= \x -> if x < 5 then loop else return x in execState loop 1
答案 1 :(得分:1)
并非所有单子都支持守卫;只有那些在MonadPlus。看起来你正在使用Identity monad(可能是execState的一部分)。当守卫失败时你想发生什么?