我有一个问题,我不知道如何推理。我只是想问一下是否有人可以帮我解决具体问题,但我突然意识到我可以提出一个更普遍的问题,希望能得到一个更好的一般性理解。希望。所以这里是:
当你的程序太懒,通常很明显,因为你最终会遇到像空间泄漏这样的明显问题。我有相反的问题:我的程序太严格了。我在尝试着 领带 结并发现我试图做的某些事情会以某种方式打败我所需要的懒惰。所以我的一般问题是, 如何调试不必要的严格性?
为了完整起见,这是我的具体案例:我在 RWS
,编写器组件填充地图,阅读器组件观察该地图的最终状态。在我完成填充之前,我无法对此地图进行任何严格的操作。在地图中查找值似乎没有问题,例如:
do
m <- ask
val <- m ! key
doSomething val -- etc.
但 (!)
没用 error
,而我更喜欢使用我的monad失败 fail
。所以我想做类似以下的事情:
do
m <- ask
maybe
(fail "oh noes")
(doSomething)
(lookup key m)
这导致我的程序 <<loop>>
,我不明白。它没有 似乎 对我来说,这应该比使用更严格 (!)
,但显然我错了......
你的第一个例子是地图中的严格。以下查找 print "1"
然后运行它,程序实际打印1.当然,这需要评估 m
。
main = do let m = Map.fromList [(1, print "1")]
val <- m ! 1
return val
你可能想写一些只读地图的东西。以下不严格,因为 val
不用于案例表达式。
main = do let m = Map.fromList [(1, print "1")]
let val = m ! 1
return val
你的第二个例子是严格的,因为它检查是否结果 lookup
成功地决定如何完成do-block的执行。这需要阅读地图。它相当于:
do m <- ask
case lookup key m of
Nothing -> fail "oh noes"
Just x -> doSomething x
调试严格性问题
评估总是由案例表达或某些内置运算符强制执行 +
对于整数。如果您怀疑您的程序因为在可用之前强制使用某个值而失败,那么您将需要找出强制使用哪个值以及强制使用它的位置。
哪个价值被迫?
在这种错误中,程序会尝试计算一个取决于其自身评估结果的表达式。您可以使用 trace
跟踪正在评估的表达式。在这个问题上,它看起来像是值 m
被迫,所以使用 trace
在评估之前打印消息:
do m1 <- ask
let m = trace "Using m" m1
...
如果“使用m”是程序的最后一个输出(在...之前) <<loop>>
),你越来越接近这个bug。如果它不在输出中,那么 m
没有被评估,所以问题出在其他地方。如果输出中的这一行后面有某些内容,则程序继续执行并稍后发生错误,因此问题必须在其他地方。
它被迫在哪里?
这告诉你评估在停止之前至少得到了这么远。但它走了多远?问题实际上发生得太晚了吗?要看到这一点,试试放一个 trace
在以后得到评估的东西上。我们知道 m
进行评估,以决定哪个分支 maybe
跑,所以我们可以 trace
在那些点上。
do m1 <- ask
let m = trace "Using m" m1
maybe (trace "Used m" $ fail "oh noes")
(\x -> trace "Used m" $ doSomething x)
(lookup key m)
如果您在输出中看到“使用m”后跟“使用过的m”,那么您就知道评估了 m
完了,程序继续进行。如果只看到“使用m”,则程序在这些点之间停止。在这种特殊情况下,您不应该看到“使用过的m”,因为 maybe
部队的评估 m
并导致 <<loop>>
。