11

我有一个问题,我不知道如何推理。我正要问是否有人可以帮助我解决具体问题,但我突然意识到我可以问一个更笼统的问题,并希望因此得到更好的总体理解。希望。所以这里是:

当您的程序太懒惰时,这通常很明显,因为您最终会遇到诸如空间泄漏之类的明显问题。我有相反的问题:我的程序太严格了。我正在尝试打结 并发现我尝试做的某些事情会以某种方式击败我需要的懒惰。所以我的一般问题是,如何调试不需要的严格性?


为了完整起见,这是我的具体情况:我在 中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>>,我不明白。在我看来,这似乎不应该比 using 更严格(!),但显然我错了......

4

1 回答 1

10

您的第一个示例在地图中是严格的。下面查找print "1",然后运行它,程序实际打印 1。当然,这需要评估m

main = do let m = Map.fromList [(1, print "1")]
          val <- m ! 1
          return val

你可能打算写一些只读取地图的东西。以下不是严格的,因为val没有在 case 表达式中使用。

main = do let m = Map.fromList [(1, print "1")]
          let val = m ! 1
          return val

您的第二个示例很严格,因为它检查结果是否lookup成功以决定如何完成执行块。这需要阅读地图。它相当于:

do m <- ask
   case lookup key m of
     Nothing -> fail "oh noes"
     Just x  -> doSomething x 

调试严格性问题

评估总是由 case 表达式或某些内置运算符(如+整数)强制执行。如果你怀疑你的程序失败是因为一个值在它可用之前就被强制了,你会想知道哪个值被强制了,它在哪里被强制了。

哪个值是强制的?

在这种错误中,程序尝试评估取决于其自身评估结果的表达式。您可以使用trace它来跟踪正在评估的表达式。在这个问题中,看起来 的值m是被强制的,所以trace在它被评估之前使用它来打印一条消息:

do m1 <- ask
   let m = trace "Using m" m1
   ...

如果“Using m”是程序的最后一个输出(在 之前<<loop>>),那么您离错误越来越近了。如果它不在输出中,那么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)

如果您在输出中看到“Using m”后跟“Used m”,那么您知道评估m完成并且程序继续运行。如果您只看到“使用 m”,则程序在这些点之间停止。在这种特殊情况下,您不应看到“Used m”,因为会maybe强制评估m并导致<<loop>>.

于 2012-07-07T06:26:19.900 回答