如何调试Haskell代码?

时间:2011-07-17 14:22:32

标签: debugging haskell

我有问题。我写了一个很大的Haskell程序,它始终适用于小输入。现在,当我想测试它并生成更大的输入时,我总是收到消息:

HsProg: Prelude.head: empty list

我多次使用Prelude.head。我该怎么做才能找到更多或获得更好的错误输出以获得它发生的代码行?

4 个答案:

答案 0 :(得分:69)

GHCi选项-fbreak-on-exception可能很有用。这是一个调试会话示例。首先,我们将文件加载到GHCi中。

$ ghci Broken.hs
GHCi, version 7.0.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
[1 of 1] Compiling Main             ( Broken.hs, interpreted )
Ok, modules loaded: Main.

现在,我们打开-fbreak-on-exceptions并跟踪我们的表达式(在这种情况下main用于整个程序。)

*Main> :set -fbreak-on-exception
*Main> :trace main
Stopped at <exception thrown>
_exception :: e = _

我们已经停止了例外。我们试着用:list来查看代码。

[<exception thrown>] *Main> :list
Unable to list source for <exception thrown>
Try :back then :list

因为Prelude.head发生了异常,我们无法直接查看来源。但是当GHCi通知我们时,我们可以去:back并尝试列出跟踪之前发生的事情。

[<exception thrown>] *Main> :back
Logged breakpoint at Broken.hs:2:23-42
_result :: [Integer]
[-1: Broken.hs:2:23-42] *Main> :list
1  
2  main = print $ head $ filter odd [2, 4, 6]
3  

在终端中,违规表达式filter odd [2, 4, 6]以粗体突出显示。所以这是在这种情况下评估为空列表的表达式。

有关如何使用GHCi调试器的更多信息,请参阅the GHC User's Guide

答案 1 :(得分:9)

您可能需要查看Haskell Wiki - Debugging,其中包含许多有用的问题解决方法。

一个很有前途的工具是LocH,它可以帮助您在代码中找到触发空列表错误的head调用。

就个人而言,我推荐safe包,它允许从Prelude中注释大多数部分函数(从而导致更有意识地使用这些部分函数)或者更好,使用head等函数的 total 变体,它们总是返回一个结果(如果输入值至少是定义)。

答案 2 :(得分:1)

您可以使用此库: Debug.Trace

您可以使用以下函数替换任何值a:

trace :: String -> a -> a

putStrLn不同的是,输出中没有IO,例如:

>>> let x = 123; f = show
>>> trace ("calling f with x = " ++ show x) (f x)
calling f with x = 123
123

trace函数应仅用于调试或监视执行。该函数不是参照透明的:它的类型表明它是一个纯函数,但具有输出跟踪消息的副作用。

答案 3 :(得分:0)

从GHC 8开始,您可以使用GHC.Stack模块或一些on a Simon's blog详细的性能分析编译器标志。