'Nice' Debugging?

Are there any nice ways to use Debug.Trace functions?

(it’s a bit of an oxymoron, I know!)

Small problem: if I insert them into my code, and want to uncomment them I usually also have to worry about whitespace again (maybe an auto formatter is a good idea).

Quite happy I have gone a long time without them, but they are handy also.

2 Likes

One trick I learned a while ago is to use ' to differentiate between traced function and untraced one. Say i have foo with some args and I want to debug it. I will change code from

foo ... = ... foo ... -- first pattern match, recursive call
foo ... = ...         -- another pattern match

to

foo ... = traceShow (..., ..., ...) $ foo' ...  -- put the arguments in the tuple and then call the original function
foo' ... = ... foo ... -- first pattern match, recursive call
foo' ... = ...         -- another pattern match

This way, I don’t need to change te rest of the code and can quickly remove tracing by commenting the first line and removing the 's

5 Likes

One trick I use often is to add a bogus guarded case to functions:

fib n | trace ("fib " ++ show n) False = undefined
fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)
20 Likes

Usually, I manage to make trace a full line, so I just have to comment the line.
Do you have any example ?

1 Like

Yes

foo = <lots of code>
    where !() = trace ("Something: " ++ show importantVariable) ()

(Always use where !() not where !_ otherwise you risk writing trace s instead of trace s () and the former doesn’t produce any output!)

15 Likes

There’s fancy wrappers in Hledger.Utils.Debug.

4 Likes

Albert Lai has a page on Debug.Trace which includes the following idiom:

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.

6 Likes

I’m not sure it counts as nice but I often add debug and performance stuff in as (the lazy) StateT, and then you can flip between evalStateT for non-debug and runStateT for debug, without having to erase the debug code.

4 Likes

Could you give an example ?

1 Like

PerfT is used for performance measurement, but you could generalize the idea.

https://hackage.haskell.org/package/perf-0.12.0.0/docs/Perf-Types.html

1 Like

This is better than my trick, one single line to comment out to disable tracing instead of also needing to remove 's. Thanks