Question

When one is programming in an imperative programming languages such as Java one can conveniently add trace statements. For example:

for (int i=0; i<10; i++) {
  // do something
  // do something
  System.out.println("Some trace statement");
  // do something 
}

How does one accomplish this in a LISP dialect such as Clojure - for example say I wanted to add a trace just before recur:

(def fact
  (fn [n]
    (loop [cnt n acc 1]
       (if (zero? cnt)
            acc
          ;; say I want to add a trace here
          (recur (dec cnt) (* acc cnt))))))

Notes:

  1. The method should be relatively as simple as adding a line
  2. For example if I were to use a do block -- I have to reformat, make sure I close the brackets appropriately
Was it helpful?

Solution

Non-invasive tracing

Lisp environments generally provide interactive debugging environment and trace mechanisms. For example, in SBCL, you could use the trace macro: you don't even need to modify your code, like you did in your Java example.

For Clojure, look at the tools.trace library, or the following answer: clojure: adding a debug trace to every function in a namespace?

Custom functions and macros

See also the many answers to this question: Debugging in Clojure? Most of them involve nesting the expression you want to debug/trace inside another expression, like Chiron suggested.

I don't think that "I have to reformat and close the brackets appropriately" is a good argument; everytime you edit your program you have to deal with the syntax, or else you won't ever modify your code.

Paredit

I personally don't use I am now a happy user of Paredit. Your editor keep track of parens and brackets while you code, which is quite handy.

Reader macros

I you really don't want to nest your expression inside another one, I suppose you could write a reader macro so that you could annotate an expression with a debug statement, but this is overkill, imho (edit: this is what spyscope does, apparently; see NielsK's answer).

OTHER TIPS

The Spyscope library provides a simple option for putting in trace prints without having to change the original syntax, just in the way you (and many others) prefer.

spyscope.repl=> (take 20 (repeat #spy/p (+ 1 2 3)))
6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6) 

There's also ways of including trace messages

spyscope.repl=> #spy/d ^{:marker "triple-add"} (+ 1 2 3)
spyscope.repl$eval3935.invoke(NO_SOURCE_FILE:1) triple-add (+ 1 2 3) => 6
6

and even (partial) stack traces

spyscope.repl=> (take 20 (repeat #spy/d ^{:fs 3} (+ 1 2 3)))
----------------------------------------
clojure.lang.Compiler.eval(Compiler.java:6477)
clojure.lang.Compiler.eval(Compiler.java:6511)
spyscope.repl$eval675.invoke(REPL:13) (+ 1 2 3) => 6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)

Use do block:

(def fact
(fn [n]
(loop [cnt n acc 1]
   (if (zero? cnt)
        acc
      (do
        (println "**")
        (recur (dec cnt) (* acc cnt)))))))

user=> (fact 4)
**
**
**
**
24

In your REPL:

(doc do)

do (do exprs*)
Special Form
Evaluates the expressions in order and returns the value of the last. If no expressions are supplied, returns nil. Please see http://clojure.org/special_forms#do

I have several macros that allow something like this (because it is handy not having to toss in a progn many times):

(my-trace ("hi mom" 1 2 3) recur (dec cnt) (* acc cnt))

my-trace simply expands into:

(progn (imperative-trace "hi mom" 1 2 3) (recur (dec cnt) (* acc cnt)))

a simpler variant is:

(echo hi-mom recur (dec cnt) (* acc cnt))

which captures the result of the wrapped form and prints it tagged with hi-mom.

A nicety is that I can insert/remove nil as the unevaluated first parameter to turn tracing off/on. On some debug out put I just leave the trace in place but disabled until the next time I need it.

-hk

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top