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).