5

这个问题本质上是使用 GHCi 在 Haskell 程序中调试无限循环的副本。那里的作者手动解决了它,但我想知道其他解决方案

(我的特殊问题)

我有一个包含递归调用的箭头代码,

testAVFunctor = proc x -> do
    y <- errorArrow "good error" -< x
    z <- isError -< y
    (passError ||| testAVFunctor) -< trace "value of z" z

应该使递归 testAVFunctor 不执行,因为这errorArrow将导致 isError 返回 a Left (AVError "good error"),它应该依次选择passError路由并绕过递归调用。

非常奇怪的是,在函数组合等流行站点插入“跟踪”调用会导致程序发出有限数量的输出,然后冻结。不是我对无限项扩展问题的期望。(见编辑1)

如果有人很好奇,我已经在这里上传了我的源代码。

编辑 1

我没有找对地方(如果您想查看源代码,显然avEither正在循环)。我到达那里的方式是编译一个二进制文件并运行 gdb

  • gdb 主要
  • r (运行代码)
  • Ctrl+C (发送中断)。回溯将是无用的,但你能做的,是命中
  • s (步骤)。然后,按住回车键;您应该会看到很多方法名称飞过。希望其中一个能够被识别。

您可以使用 ghc 标志进行编译-O0以禁用优化,这可以显示更多方法名称。

编辑 3

显然,proc x -> do上面的代码块导致代码生成组合子,这些组合子正在调用AVFunctor.arr要调用的提升方法——其中的某些东西一定违反了惰性。如果我将顶级函数重写为

testAVFunctor = errorArrow "good error" >>>
    isError >>> (passError ||| testAVFunctor)

然后一切正常。我想是时候尝试学习和使用garrows(伯克利的一名研究生)。

我从经验中得出的一般结论是 ghci 调试可能会令人沮丧。例如,我设法将参数显示fAVFunctor.arr局部变量,但我无法从中获得任何非常有用的信息:

> :i f
f :: b -> c     -- <no location info>

修改后的源代码在这里

4

1 回答 1

3

请记住, 的含义(|||)取决于箭头,并且testAVFunctor是箭头的无限对象:

testAVFunctor = proc x -> do
    ...
    (passError ||| proc x -> do
                       ...
                       (passError ||| proc x -> ...) -< trace "value of z" z)
        -< trace "value of z" z

我不确定你是否意识到这一点。检查(|||)(或者如果没有,left)的定义,看看它是否可以处理无限项。还要检查(>>>)(呃,(.)我认为在现代版本中)。确保组合子不是严格的,因为那样无限项会发散。这可能涉及使模式更懒惰~(在使用箭头时我不得不这样做很多)。您看到的行为可能是由于其中一个组合器过于严格造成的,因此它评估“足够远”以提供一些输出,但稍后会卡住。

祝你好运。你进入了 Haskell 的深层微妙之处。

于 2011-05-01T05:59:32.347 回答