Haskell:RWS上的一元不动点在遍历参数时是循环的
我正在写一个程序,其中包括Haskell:RWS上的一元不动点在遍历参数时是循环的,haskell,monads,lazy-evaluation,fixed-point-iteration,monadfix,Haskell,Monads,Lazy Evaluation,Fixed Point Iteration,Monadfix,我正在写一个程序,其中包括RWS,用于跟踪可变状态并生成一些日志。我的目的是定义一个计算,用于评估某个操作,收集后续状态,并根据它将某些内容附加到编写器的日志开头。最简单的例子: 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 -> p
RWS
,用于跟踪可变状态并生成一些日志。我的目的是定义一个计算,用于评估某个操作,收集后续状态,并根据它将某些内容附加到编写器的日志开头。最简单的例子:
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)
这种行为对我来说很奇怪,我不明白为什么这个计算会发散。由于第二个变量中的前置
在任何程度上都不会改变状态,因此更难证明这一点。为什么这个程序不收敛?我能做些什么来修复它吗
我知道我可以使用状态RWS
的一部分来解决它,但出于某种原因,我想避免它。表单u
只有在定义了s
时才被定义,但这里s
是mfix
传递的占位符,只有当整个计算prepend s>>act>>get
终止时,才会定义
您的第一个版本可以工作,因为它不需要检查状态就可以将其配对
mfix::(a->ma)->ma
不接受严格的函数f::a->ma
(即f undefined=undefined
)
如果你有一系列的事情要告诉你,那么一个更懒散的方法就是在告诉他们之前把它们串联起来:
prepend s = tell (concatMap show s)
这是因为表单
并不懒惰。此要求在中明确调用:函数必须是惰性的,以使不动点收敛。但是表单
确实需要对其参数进行解构,以便对其进行迭代。它仍然可以对列表中的每个元素保持惰性,但列表本身却不能
当您运行此递归ish计算时,它需要三个步骤(即三个一元绑定):prepend
,然后act
,然后get
,因此您基本上得到如下值:
[foldMap show s, "a", "b"]
其中,foldMap show s
块尚未评估-即,它是指向s
的砰砰声,这是相同计算的最终状态。在对状态求值之前,可以引用状态以将其合并到foldMap show s
表达式中,因为Haskell是惰性的。这就是工作中的懒惰
但是,如果将prepend
替换为foldM
,则计算中不再有三个步骤(三个一元绑定)。现在,结果状态列表的每个元素都有一个步骤。这意味着为了构造计算(即定义其步骤,aka binds),您需要检查其自身的结果状态