gpt4 book ai didi

Haskell:如果遍历参数,RWS 上的单子(monad)固定点将循环

转载 作者:行者123 更新时间:2023-12-02 07:59:17 25 4
gpt4 key购买 nike

我正在编写一个涉及 RWS 的程序,用于跟踪可变状态并生成一些日志。我的目的是定义一个计算来评估某些操作,收集后续状态并根据它向 Writer 日志的开头追加一些内容。最小示例:

type M = RWS () String [Int]

run a = evalRWS a () []

prepend s = tell (foldMap show s)

act = do
tell "a"
modify (1:)
tell "b"

comp = mfix $ \s -> prepend s >> act >> get

在这里,我使用 MonadFix 通过在 act 发生之前写入日志来改变过去。它可以完美地返回 "1ab"。但是,如果我使用 M 遍历状态,那么它会挂起:

prepend s = forM_ s (tell . show)

这种行为对我来说很奇怪,我不明白为什么这种计算会发散。更难证明,因为第二个变体中的 prepend 不会在任何程度上改变状态。为什么这个程序不收敛?我能做些什么来修复它(inb4“hehe fix”)吗?

我知道我可以使用 RWSState 部分解决它,但出于某种原因我想避免它。

最佳答案

发生这种情况是因为 forM_ 不是惰性。此要求在 the mfix documentation 中明确提出:函数必须是惰性的才能使不动点收敛。但是 forM_ 确实需要解构它的参数以便迭代它。它仍然可以对列表的每个元素保持惰性,但不是列表本身。


当你运行你的这种递归式计算时,它需要三个步骤(即三个单子(monad)绑定(bind)):prepend,然后是 act,然后是 get ,因此您基本上得到如下所示的值:

[foldMap show s, "a", "b"]

foldMap show s 部分尚未计算 - 即它是指向 s 的 thunk,这是相同计算的最终状态。甚至可以在评估状态之前引用状态将其合并到 foldMap show s 表达式中,因为 Haskell 是惰性的。这是工作中的懒惰。

但是,如果将 prepend 替换为 foldM_,则计算中不再有三个步骤(三个单子(monad)绑定(bind))。现在,您对结果状态列表的每个元素都有一个步骤。这意味着甚至为了构建计算(即定义其步骤,也称为绑定(bind)),您需要检查其自身的结果状态。

关于Haskell:如果遍历参数,RWS 上的单子(monad)固定点将循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59578000/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com