- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个涉及 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”)吗?
我知道我可以使用 RWS
的 State
部分解决它,但出于某种原因我想避免它。
最佳答案
发生这种情况是因为 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/
我是一名优秀的程序员,十分优秀!