[Haskell-cafe] Debug tracing in Haskell

Corentin Dupont corentin.dupont at gmail.com
Tue Nov 22 14:58:52 UTC 2016

Hi Clinton,
I've been running into the same problem recently: regularly adding and then
removing a lot of traces from my code.

Personally I don't like adding conditional compilation/cpp in my code (it's
quite ugly and difficult to maintain).
I've been looking into a logging packages:

The question is: how to carry around the settings of the logger (i.e. the
log levels: Warn, Fatal...) in your program?
I usually structure my programs around a big State monad, that I access
using Lenses. The log level could be added there.

On Tue, Nov 22, 2016 at 2:37 PM, Clinton Mead <clintonmead at gmail.com> wrote:

> I've been debugging some Haskell code, including a lot of work on Mutable
> Vectors, so naturally I've found `trace` useful, and in particular
> `traceM`, because it fits in nicely with monadic code.
> The problem is that unlike `assert`, both `trace` and `traceM` execute
> unconditionally, I can't toggle them with a compiler flag.
> I could however do something like this in every file I want to do tracing.
> {-# LANGUAGE CPP #-}
> #ifdef TRACE
> trace = Debug.Trace.trace
> traceM = Debug.Trace.traceM
> #else
> trace _ = id
> traceM _ = pure ()
> #endif
> But already, that's a lot of boilerplate.
> I'd also like to trace based on debug levels. For example, at TRACE level
> 2, only print trace statements at level 1 or 2, but at TRACE level 4, print
> trace statements at level 1, 2, 3 and 4, providing more detail (but more
> noise).
> This makes the above code even more complex.
> This wouldn't be a problem if I could put the code in a separate package,
> but I can't, as then whether tracing is on or not depends on the compiler
> settings of the tracing package, not on the calling package, which defeats
> the purpose somewhat.
> I considered using implicit parameters to quietly pass whether I want
> tracing and at what level into the tracing module, but it seems to be that
> implicit parameters can't be defined at the top level.
> It gets even more complex. When debugging, I might want to print something
> of type 'a'. Obviously 'a' will need some sort of show method for this to
> work, but outside of debug mode I don't want to restrict my function's
> types to things which are showable. So I considered doing this:
> #ifdef TRACE
> type DebugShow a = Show a
> debugShow = show
> #else
> type DebugShow a = ()
> #endif
> And of course, if you're only using `debugShow` as part of an argument to
> `trace`, lazy evaluation will avoid `debugShow` ever being called when
> tracing is not enabled.
> But put all this together and you've got around a dozen lines of
> boilerplate just to do tracing, without even having tracing levels yet,
> that have to be put in every module you want to use tracing, but don't want
> a whole lot of debug data being spat out in a release compile.
> Also, adding any other tracing functions just makes this longer.
> The only approach I can think of so far is to whack this all in a template
> haskell module and add code that splices it all into the current module:
> e.g.
> #ifdef TRACE
> $(traceFunctions True)
> #else
> $(traceFunctions False)
> #endif
> Where `traceFunctions` is a template haskell function that dumps all the
> appropriate functions and type definitions mentioned above (and maybe more)
> at the top level of the calling module.
> So my questions are:
> 1. Is there a better way? And
> 2. Has this problem already been solved?
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20161122/aa020610/attachment.html>

More information about the Haskell-Cafe mailing list