跟踪Haskell中的错误

时间:2010-01-26 13:02:53

标签: haskell

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

有一次,我跑了“主”并回来了

*** Prelude.read: parse error

没有其他信息。幸运的是,我知道我只是在一个地方打电话阅读并且能够修复它,但是对于未来:

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

谢谢!

修改使用GHC。

5 个答案:

答案 0 :(得分:8)

如果你可以在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执行不会使用类似于普通调用堆栈的任何东西,因此要获得有关异常的那种信息,您必须在某种特殊模式(调试或分析)中运行,这样可以保留这种信息。

答案 1 :(得分:6)

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

import Debug.Trace (trace)

--change
myRead s = read s
--to 
myRead s = trace s (read s)
--or 
myRead s = trace (take 100 s) (read s)

答案 2 :(得分:3)

通常,您可以通过这样的方式处理错误,即有足够的上下文来调试原因。

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

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

最后,在你的特定情况下read抛出一个异常,所以你必须捕获它然后处理调用代码中的错误(看看Control.Exception包)。

答案 3 :(得分:2)

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

Haskell中的堆栈跟踪并不简单,因为它的懒惰。尽管如此,上述调试器提供了一些工具(参见上面URL中的2.5.5。跟踪和历史记录)。

答案 4 :(得分:1)

您可以考虑使用另一个StackOverflow用户"Practical Haskell: shell scripting with error handling and privilege separation"dons中使用monadic read

  

第一步是将read替换为提升为通用错误monad 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]