有人能告诉我为什么这段使用 State monad 的代码永远不会结束吗?
fib' :: State [Int] ()
fib' = do l <- get
let l2 = sum (last2 l)
put (l ++ [l2])
return ()
fib'
take 10 $ execState fib' [0, 1]
当我在 ghci REPL 中执行它时,该函数执行时不会停止。
的结果execState
是计算结束时的状态值。但是你的计算永远不会结束:fib'
调用fib'
. 虽然在您的情况下可以证明状态的前 10 个元素最终不会改变,但编译器无法预测(也不应该)。
您可能想查看 Writer Monad,您可以在其中懒惰地处理写入的数据。
return
不像在命令式语言中那样工作。对于每个 monad,它的定义不同,对于 state monad,它被定义为:
return x = State $ \s -> (x,s)
所以它实际上并没有脱离fib'
函数,而是将一个新的 State monad 绑定到计算的其余部分。
编辑:
虽然我最初的答案是正确的,但它并没有回答 OP 的问题。在 OP 的代码中,return
充当无操作。对于 OP 的代码永远不会终止的原因,请参阅其他答案。我仍然在这里留下我的答案,因为我觉得指出它仍然很重要,因为它return
确实不必要地出现在 OP 的代码示例中
return ()
线是多余的,可以删除(++)
几乎总是一个坏主意,因为它的第一个参数的大小是 O(N)。您在这里所做的或多或少等同于以下内容:
rfib (last:last':rest) = let new = last + last' in rfib (new:last:last':rest)
它试图“懒惰”地以相反的顺序创建数字。(顺序并不重要,我只是反过来做以摆脱分散注意力的(++)和last2)。
但关键是,你永远不会计算一个列表,因为类型检查器会告诉你。例如,以下都是很好的类型:
res1 = (take 10 . rfib) [1,0] :: [Int]
res2 = (take 10 . snd . rfib) [1,0] :: [Int]
res3 = (take 10 . snd . snd . rfib) [1,0] :: [Int]
res4 = "foo" == rfib [1,0]
你知道,有时不写注释是个好主意。然后你会看到类似这样的推断:
rfib :: Num a => [a] -> b
这与您的 foo' 的主要类型密切相关:
fib' :: Num a => State [a] b
这种类型的你应该考虑一下。它的情况rfib
告诉你,你不知从哪里创建了一个你想要的任何类型的值,这里称为b
. 这就是“没有这样的价值”的同义词。与head []
orunJust Nothing
或一样error "Bottom"
,任何计算该值的尝试都必须发散。
当结果中出现的新类型变量被类型构造函数“保护”时,情况就不同了。然后会发生什么将取决于类型,其构造函数被应用。就目前而言,它适用于 Writer,但不适用于 State。尽管如此,这种出乎意料的类型应该让人们重新考虑一下。
在@Joachim Breitner 的建议下,使用惰性 Writer monad 的可能解决方案是:
import Control.Monad.Writer
fib' :: Writer [Int] ()
fib' = tell [1, 1] >> go 1 1
where
go :: Int -> Int -> Writer [Int] ()
go f1 f2 = do
let f3 = f1 + f2
tell [f3]
go f2 f3
main = print $ take 10 $ execWriter fib'