Contents

Top

Debug printing in Haskell

Most Haskell expressions are not supposed to perform I/O, unless by a backdoor (please don't abuse it). But we recognize that debug printing is a reasonable use of the backdoor.

The debug printing functions are in Debug.Trace. I only talk about trace, traceShow, traceShowId. They cover most cases.

I will illustrate with this function:

f n | n <= 1 = n
    | r == 0 = f q
    | otherwise = f (3*n + 1)
  where
    (q, r) = n `divMod` 2

trace :: String -> a -> a

trace msg x equals x but also prints msg. I use it to print out the cases hit:

import Debug.Trace

f n | n <= 1 = trace "base case" n
    | r == 0 = trace "even case" (f q)
    | otherwise = trace "odd case" (f (3*n + 1))
  where
    (q, r) = n `divMod` 2

Sample session:

prompt> f 10
even case
odd case
even case
even case
even case
even case
base case
1

If you want the messages to be more informative, e.g., also printing the values of n, it can be done:

f n | n <= 1 = trace ("base case n=" ++ show n) n
    | r == 0 = trace ("even case n=" ++ show n) (f q)
    | otherwise = trace ("odd case n=" ++ show n) (f (3*n + 1))
  where
    (q, r) = n `divMod` 2

Sample session:

prompt> f 10
even case n=10
odd case n=5
even case n=16
even case n=8
even case n=4
even case n=2
base case n=1

traceShow :: Show a => a -> b -> b

traceShow n x equals x but also prints n. I use it to print the values of n:

import Debug.Trace

f n | n <= 1 = traceShow n n
    | r == 0 = traceShow n (f q)
    | otherwise = traceShow n (f (3*n + 1))
  where
    (q, r) = n `divMod` 2

Sample session:

prompt> f 10
10
5
16
8
4
2
1
1

If I am tired of writing “traceShow n” 3 times (and later deleting them thoroughly), here is a cool trick:

import Debug.Trace

f n | traceShow n False = undefined
f n | n <= 1 = n
    | r == 0 = f q
    | otherwise = f (3*n + 1)
  where
    (q, r) = n `divMod` 2

Explanation: A new case that the computer must check first, which will be summarily rejected anyway, but it's the journey of printing n that matters.

Benefits: easy and unintrusive to add, and easy to delete later.

traceShowId :: Show a => a -> a

traceShowId x equals x and also prints it. I use it to print the results of (q, r) = n `divMod` 2:

import Debug.Trace

f n | n <= 1 = n
    | r == 0 = f q
    | otherwise = f (3*n + 1)
  where
    (q, r) = traceShowId (n `divMod` 2)

Sample session:

prompt> f 10
(5,0)
(2,1)
(8,0)
(4,0)
(2,0)
(1,0)
1

This also shows an effect of lazy evaluation: when n=1, q and r are unused, n `divMod` 2 is not evaluated, so there is no printing of (0,1).