【问题标题】:Tracking down errors in Haskell在 Haskell 中追踪错误
【发布时间】:2011-01-09 12:21:09
【问题描述】:

如何获得有关 Haskell 错误发生位置的更多信息?例如,昨天我正在开发一个 Haskell 程序,它解析输入文件、转换数据然后打印出报告信息。

有一次,我跑了“main”然后回来了

*** Prelude.read: parse error

没有其他信息。幸运的是,我知道我只在一个地方调用了 read 并且能够修复它,但是为了将来:

  • 是否可以获得此类错误的回溯或行号?
  • 是否可以获得触发错误的实际数据,即导致解析错误的字符串?

谢谢!

编辑使用 GHC。

【问题讨论】:

  • 你最好完全避免使用部分函数。请改用Safe.readMay
  • 查看here以获得更好的解决方案

标签: haskell


【解决方案1】:

如果你可以在 ghci 中运行代码,那么调试器就可以做任何你想做的事情。这是一个引发异常的程序

foo s i
  | i == 57 = read s
  | otherwise = i
main = mapM_ (print . foo "") [1..100]

现在将其加载到 ghci 并使用调试器,如下所述: http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-debugger.html#ghci-debugger-exceptions

> ghci test.hs
*Main> :set -fbreak-on-error
*Main> :trace main
1
2
... snipped 3 through 55 ...
56
Stopped at <exception thrown>
_exception :: e = _
[<exception thrown>] *Main> :back
Logged breakpoint at test.hs:2:15-20
_result :: a
s :: String
[-1: test.hs:2:15-20] *Main> :list
1  foo s i
2    | i == 57 = **read s**
3    | otherwise = i
[-1: test.hs:2:15-20] *Main> s
""
[-1: test.hs:2:15-20] *Main> 

它可以让您在评估历史记录中四处走动,突出显示引发异常的实际表达式(粗体而不是在终端上加星标),并让您检查局部变量。

另一个选项是使用分析和一些标志重新编译以标记适当的成本中心,并使用 -xc 分析选项运行,该选项在未捕获的异常上打印成本中心堆栈 http://www.haskell.org/ghc/docs/latest/html/users_guide/prof-time-options.html

> ghc -prof -auto-all test.hs
> ./test +RTS -cs
1
2
... snipped 3 through 55 ...
56
*** Exception (reporting due to +RTS -xc): (THUNK_2_0), stack trace: 
  Main.foo,
  called from Main.main,
  called from Main.CAF
  --> evaluated by: Main.main,
  called from Main.CAF
test: Prelude.read: no parse

这有点困难的原因在前面的调试器页面上有所描述 http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci-debugger.html#tracing 基本上,高效的 Haskell 执行不使用任何类似于普通调用堆栈的东西,因此要获取有关异常的那种信息,您必须在某种特殊模式(调试或分析)下运行,它确实保留了这种信息。

【讨论】:

    【解决方案2】:

    您可以通过导入Debug.Trace 并更改来获取导致解析错误的字符串 你的电话

    导入 Debug.Trace(跟踪) - 改变 myRead s = 读取 s - 到 myRead s = trace s(读取 s) - 要么 myRead s = trace (take 100 s) (read s)

    【讨论】:

    • 我发现\s-&gt;fst$head$reads s++[error$"Can't read: "++show s] 更有用,因为它在读取成功时不会发送垃圾邮件。
    【解决方案3】:

    一般而言,由您来处理错误,以便有足够的上下文供您调试原因。

    Haskell 的惰性使得堆栈跟踪难以实现,因为在错误发生时调用堆栈可能不再存在。

    一种简单的错误处理方法是使用 Either 类型,它允许您在事情顺利时返回一个值,或者在发生错误时返回一些上下文(错误消息、输入字符串...)。

    最后,在您的特定情况下,read 正在引发异常,因此您必须捕获该异常,然后在调用代码中处理错误(查看 Control.Exception 包)。

    【讨论】:

      【解决方案4】:

      您没有告诉我们您使用的是哪个编译器。如果你使用GHC,那么你应该看看GHCi Debugger

      Haskell 中的堆栈跟踪并非微不足道,因为它具有惰性。尽管如此,上述调试器还是提供了一些工具(参见上面 URL 中的第 2.5.5 节。跟踪和历史记录)。

      【讨论】:

        【解决方案5】:

        您可以考虑使用一元 read,如 StackOverflow 用户 dons 中的 "Practical Haskell: shell scripting with error handling and privilege separation" 中的那样:

        第一步是将read 替换为提升为通用错误单子的版本MonadError

        readM :: (MonadError String m, Read a) => String -> m a
        readM s | [x] <- parse = return x
                | otherwise    = throwError $ "Failed parse: " ++ show s
            where
                parse = [x | (x,t) <- reads s]
        

        【讨论】:

          猜你喜欢
          • 2011-11-05
          • 2011-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多