调试不必要的严格性?

mer*_*ict 11 haskell lazy-evaluation strictness tying-the-knot

我有一个问题,我不知道如何推理.我只是想问一下是否有人可以帮助我解决具体问题,但我突然意识到我可以提出一个更普遍的问题,希望能得到一个更好的一般性理解.希望.所以这里:

当你的程序太懒,通常很明显,因为你最终会遇到像空间泄漏这样的明显问题.我有相反的问题:我的程序太严格了.我想 ,并发现某些事情,我试图这样做会以某种方式打败我需要的懒惰.所以我的一般问题是,如何调试不必要的严格性?


为了完整起见,这是我的具体情况:我在RWS,编写器组件填充地图,阅读器组件观察该地图的最终状态.在我完成填充之前,我不能对这张地图做任何严格的事情.在地图中查找值似乎没有问题,例如:

do
  m <- ask
  val <- m ! key
  doSomething val -- etc.
Run Code Online (Sandbox Code Playgroud)

但是(!)没有使用error,我更愿意使用我的monad失败fail.所以我想做类似以下的事情:

do
  m <- ask
  maybe
    (fail "oh noes")
    (doSomething)
    (lookup key m)
Run Code Online (Sandbox Code Playgroud)

这导致我的程序<<loop>>,我不明白.在我看来,这不应该比使用更严格(!),但显然我错了......

Hea*_*ink 10

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

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

你可能想要写一些只能读取地图的东西.以下内容并不严格,因为val在case表达式中没有使用.

main = do let m = Map.fromList [(1, print "1")]
          let val = m ! 1
          return val
Run Code Online (Sandbox Code Playgroud)

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

do m <- ask
   case lookup key m of
     Nothing -> fail "oh noes"
     Just x  -> doSomething x 
Run Code Online (Sandbox Code Playgroud)

调试严格性问题

评估始终由案例表达式或某些内置运算符(如+整数)强制执行.如果您怀疑程序失败,因为值在可用之前被强制执行,您将需要找出强制使用哪个值以及强制使用哪个值.

哪个价值被迫?

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

do m1 <- ask
   let m = trace "Using m" m1
   ...
Run Code Online (Sandbox Code Playgroud)

如果"使用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)
Run Code Online (Sandbox Code Playgroud)

如果在输出中看到"使用m"后跟"使用过的m",那么您就知道已m完成的评估和程序继续进行.如果只看到"使用m",则程序在这些点之间停止.在这种特殊情况下,你不应该看到"使用过的m",因为maybe力量的评估m和导致了<<loop>>.