我有一个问题。我写了一个很大的Haskell程序,它总是能处理小输入。但现在,当我想测试它并生成更大的输入时,我总是收到以下消息:
HsProg: Prelude.head: empty list
我经常使用Prelude.head
。有什么方法可以找出更多信息或获得更好的错误输出以获取发生错误的代码行?
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
命令来尝试列出跟踪中发生的先前事件。
因为异常发生在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调试器的更多信息,请参阅 GHC用户指南。您可能想要查看Haskell Wiki - Debugging,其中包含许多有用的方法来解决您的问题。
一个有前途的工具是LocH,它可以帮助您定位代码中触发“空列表”错误的head
调用。
个人建议使用safe包,它允许注释大多数Prelude中的partial函数(从而导致更加谨慎地使用这些partial函数),或者更好的是,使用总的函数变体,例如head
,它们始终返回结果(如果输入值至少被定义)。
head
,特别是对于刚接触这门语言的人。 - C. A. McCannhead
开始,并警告不要将其应用于空列表... :-/ - hvrtail
、(!!)
和其他一些东西)从Prelude
中完全删除,并且根本不向初学者提及它。但是,唉。 - C. A. McCannloch
在GHC 7中无法构建。loch-th
对我来说可以工作。但似乎它没有像loch
一样的预处理器支持。 - edwardwtrace :: 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
追踪函数只应用于调试或监控执行。该函数不是引用透明的:其类型表明它是一个纯函数,但它具有输出追踪消息的副作用。
head
通常听起来都不是一个好主意。 - C. A. McCannhead
可能值得单独在 Stack Overflow 上提问。虽然您可能不想使用任何一个版本,而是坚持使用模式匹配。 - Tyler