Here’s the development of a tiny little macro that is actually pretty useful. The post is quite verbose, because I explain a lot.

I want a debug function that first prints the “quoted” (unevaluated) code and then what it evaluates to, so I can write something like this:

user> (dbg (+ 1 2)) dbg: (+ 1 2)=3 3

This can be useful in iterations, for example, to check whether you get what you expect in each step:

user> (for [i (range 5)] (dbg (+ 1 i))) (dbg: (+ 1 i)=1 dbg: (+ 1 i)=2 dbg: (+ 1 i)=3 dbg: (+ 1 i)=4 dbg: (+ 1 i)=5 1 2 3 4 5)

Quoting is best explained using an example:

user> (+ 1 2 3) 6 user> (quote (+ 1 2 3)) (+ 1 2 3)

I’m also going to use the let form, to get convenient temporary access to an expression for the duration of the let scope. Outside the let scope, the symbol is not available:

user> (let [x (+ 1 2 3)] (println x)) 6 nil user> x Unable to resolve symbol: x in this context

So what I want is code that looks something like this:

(let [x body] (println (str "dbg: " (quote body) "=" x)) x)

In order to get the code in, we declare it as an argument using the varargs symbol (&), which will give us body as a list of forms (expressions):

(defsomething dbg [& body] (let [x body] (println (str "dbg: " (quote body) "=" x)) x))

If we declare this as a function using defn , the arguments ( body ) will be evaluated before the function is called. We don’t want that, so we instead use defmacro to get control over the evaluation. Using the macro syntax quote ( ` ), we can actually write the code exactly as we want it, and then simply unquote ( ~ ) the body symbol to get the symbol value:

(defmacro dbg [& body] `(let [x ~body] (println (str "dbg: " (quote ~body) "=" x)) x))

However, varargs are delivered as a list, so simply unquoting body will give us some strange result. We can check it using macroexpand-1 . You’ll see that the macro tries to evaluate body by placing the list inside a list: ((+ 1 2)) :

user> (macroexpand-1 '(dbg (+ 1 2))) (let [user/x ((+ 1 2))] (println (str "dbg: " (quote ((+ 1 2)))) "=" user/x)) user/x)

What we want is the contents of the varargs list as separate elements, not as a list. We change the unquote operator ( ~ ) to the unquote-splicing operator ( ~@ ):

(defmacro dbg [& body] `(let [x ~@body] (println (str "dbg: " (quote ~@body) "=" x)) x))

We expand the macro again, and now it looks better:

user> (macroexpand-1 '(dbg (+ 1 2))) (let [user/x (+ 1 2)] (println (str "dbg: " (quote (+ 1 2)))) "=" user/x)) user/x)

However, note the fully namespace-qualified symbol user/x . Clojure will not allow this due to the risks of name conflicts. When we try to run this macro, we will get an exception: Can’t let qualified name: user/x . We need some way of generating a unique symbol on the fly. We use the symbol generation operator ( # ):

(defmacro dbg [& body] `(let [x# ~@body] (println (str "dbg: " (quote ~@body) "=" x#)) x#))

We test it:

user> (macroexpand-1 '(dbg (+ 1 2))) (let [x__2071__auto__ (+ 1 2)] (println (str "dbg: " (quote (+ 1 2)) "=" x__2071__auto__)) x__2071__auto__)

OK, so the final macro looks like this:

(defmacro dbg [& body] `(let [x# ~@body] (println (str "dbg: " (quote ~@body) "=" x#)) x#))

This boils down to two very simple rules:

Any temporary symbols must be appended with # to become unique symbols

Any varargs arguments must be unquote-spliced using ~@

Let’s try our macro:

user> (dbg (+ 1 2)) (+ 1 2)=3 3

Let’s confirm that the debug functionality does not affect others using the dbg’d code:

user> (* 3 (dbg (+ 1 2))) dbg: (+ 1 2)=3 9

Now, let’s try the dbg macro on some more advanced code. Here’s an implementation of factorial using loop-recur :

(def factorial (fn [n] (loop [cnt n acc 1] (if (zero? cnt) acc (recur (dec cnt) (* acc cnt))))))

To check what happens inside the recur function, I add calls to dbg :

(def factorial (fn [n] (loop [cnt n acc 1] (if (zero? cnt) acc (recur (dbg (dec cnt)) (dbg (* acc cnt)))))))

Calling it gives:

user> (factorial 5) dbg: (dec cnt)=4 dbg: (* acc cnt)=5 dbg: (dec cnt)=3 dbg: (* acc cnt)=20 dbg: (dec cnt)=2 dbg: (* acc cnt)=60 dbg: (dec cnt)=1 dbg: (* acc cnt)=120 dbg: (dec cnt)=0 dbg: (* acc cnt)=120 120

Update:



There’s actually no point in having body be a varargs. In fact, it’s incorrect to allow more than one form, since the binding in the let form expects a single form. The macro should be:

(defmacro dbg [body] `(let [x# ~body] (println (str "dbg: " (quote ~body) "=" x#)) x#))

It could be shortened further by using the short form for quote :

(defmacro dbg [body] `(let [x# ~body] (println (str "dbg: " '~body "=" x#)) x#))

We can also skip the str call, and accept the println formatting:

(defmacro dbg [body] `(let [x# ~body] (println "dbg:" '~body "=" x#) x#))

Let’s verify that it still works:

user> (* 3 (dbg (+ 1 2))) dbg: (+ 1 2) = 3 9