53

I have a problem. I wrote a big Haskell program, and it always works with small input. Now, when I want to test it and generate a bigger input, I always get the message:

HsProg: Prelude.head: empty list

I use Prelude.head many times. What can I do to find out more or get a better error output to get the code line in which it happens?

hammar
  • 138,522
  • 17
  • 304
  • 385
haskellNewcommer
  • 555
  • 1
  • 4
  • 5

4 Answers4

84

The GHCi option -fbreak-on-exception can be useful. Here's an example debugging session. First we load our file into 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.

Now, we turn on -fbreak-on-exceptions and trace our expression (main in this case for the whole program).

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

We've stopped at an exception. Let's try to look at the code with :list.

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

Because the exception happened in Prelude.head, we can't look at the source directly. But as GHCi informs us, we can go :back and try to list what happened before in the trace.

[<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  

In the terminal, the offending expression filter odd [2, 4, 6] is highlighted in bold font. So this is the expression that evaluated to the empty list in this case.

For more information on how to use the GHCi debugger, see the GHC User's Guide.

Janus Troelsen
  • 20,267
  • 14
  • 135
  • 196
hammar
  • 138,522
  • 17
  • 304
  • 385
10

You may want to take a look at Haskell Wiki - Debugging, which contains many useful approaches to your problem.

One promising tool is LocH, which would would help you locate the head invocation in your code which triggered the empty list error.

Personally, I recommend the safe package, which allows to annotate most partial functions from the Prelude (and thus leads to a more conscious use of those partial functions) or better yet, use the total variants of functions such as head which always return a result (if the input value was defined at least).

hvr
  • 7,775
  • 3
  • 33
  • 47
  • 4
    I'd first recommend not using `head` at all, especially for someone new to the language. – C. A. McCann Jul 17 '11 at 14:42
  • 1
    @camccann I fully agree... alas most introductory material I've seen (including the rather modern LYAH) starts out with explaining `head` and just warns about being careful not to apply it to empty lists... :-/ – hvr Jul 17 '11 at 14:46
  • 1
    If I had *my* way I'd remove it (along with `tail`, `(!!)`, and some other stuff) from the `Prelude` entirely and never mention it to beginners at all. But oh well. – C. A. McCann Jul 17 '11 at 14:51
  • @hvr, ```loch``` fails to build in GHC 7. ```loch-th``` works for me, though. But seems it has no preprocessor support as ```loch``` had. – edwardw Jul 17 '11 at 16:36
  • 2
    Your LocH link is giving me a 403 error, but I assume http://hackage.haskell.org/package/loch is the same tool? – Tyler Jul 19 '11 at 02:03
  • @MatrixFrog, yes it is the same tool, but that page contains usage examples... seems they have changed the access config recently, but you can still access it [via archive.org](http://web.archive.org/web/20090616055853/http://www.cse.unsw.edu.au/~dons/loch.html) – hvr Jul 19 '11 at 06:03
2

Since GHC 8, you can use the GHC.Stack module or some profiling compiler flags detailed on a Simon's blog.

Janus Troelsen
  • 20,267
  • 14
  • 135
  • 196
2

You can use this library: Debug.Trace

You can replace any value a with the function:

trace :: String -> a -> a

unlike putStrLn there is no IO in the output, e.g.:

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

The trace function should only be used for debugging, or for monitoring execution. The function is not referentially transparent: its type indicates that it is a pure function but it has the side effect of outputting the trace message.

Yan.F
  • 630
  • 5
  • 20