views:

1504

answers:

6

Hi,

What are best ways to Debug Clojure code, while using the repl in Clojure-box ?

+11  A: 

My favourite method is a liberal sprinkling of printlns all over the code... Turning them on and off is easy thanks to the #_ reader macro (which makes the reader read in the following form, then pretend it's never seen it). Or you could use a macro expanding either to a passed-in body or nil depending on the value of some special variable, say *debug*:

(defmacro debug-do [& body]
  (when *debug*
    `(do ~@body)))

With a (def *debug* false) in there, this will expand to nil. With true, it'll expand to body wrapped in a do.


The accepted answer to this SO question: Idiomatic Clojure for progress reporting? is very helpful when debugging sequence operations.


Then there's something which is currently incompatible with swank-clojure's REPL, but is too good not to mention: debug-repl. You can use it in a standalone REPL, which is easy to get e.g. with Leiningen (lein repl); and if you're launching your programme from the command line, then it's going to bring its own REPL up right in your terminal. The idea is that you can drop the debug-repl macro in anywhere you like and have it bring up its own REPL when the programme's execution reaches that point, with all locals in scope etc. A couple of relevant links: The Clojure debug-repl, Clojure debug-repl tricks, how 'bout a debug-repl (on the Clojure Google group), debug-repl on Clojars.


swank-clojure does an adequate job of making SLIME's built-in debugger useful when working with Clojure code -- note how the irrelevant bits of the stacktrace are greyed out so it's easy to find the actual problem in the code being debugged. One thing to keep in mind is that anonymous functions without "name tags" appear in the stacktrace with basically no useful information attached to them; when a "name tag" is added, it does appear in the stacktrace and all is well again:

(fn [& args] ...)
vs.
(fn tag [& args] ...)

example stacktrace entries:
1: user$eval__3130$fn__3131.invoke(NO_SOURCE_FILE:1)
vs.                ^^
1: user$eval__3138$tag__3139.invoke(NO_SOURCE_FILE:1)
                   ^^^
Michał Marczyk
Actually there's a version of the debug-repl that works with swank now: http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml(Spoiler alert: it's awesome)
technomancy
Right, and it's good to have a link here, thanks! Agreed on awesome. :-)
Michał Marczyk
+6  A: 

I have a little debugging macro that I find very useful:

;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))

You can insert it wherever you want to watch what's going on and when:

;; Examples of dbg
(println (+ (* 2 3) (dbg (* 8 9))))
(println (dbg (println "yo")))
(defn factorial[n] (if (= n 0) 1 (* n (dbg (factorial (dec n))))))
(factorial 8)

(def integers (iterate inc 0))
(def squares  (map #(dbg(* % %))   integers))
(def cubes    (map #(dbg(* %1 %2)) integers squares))
(take 5 cubes)
(take 5 cubes)
John Lawrence Aspden
That's beautiful!
Torbjørn
Thanks! Helpful indeed.
LarsH
+16  A: 

There's also dotrace, which allows you to look at the inputs and outputs of selected functions.

(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))

produces the output:

TRACE t4425: (fib 3)
TRACE t4426: |    (fib 2)
TRACE t4427: |    |    (fib 1)
TRACE t4427: |    |    => 1
TRACE t4428: |    |    (fib 0)
TRACE t4428: |    |    => 0
TRACE t4426: |    => 1
TRACE t4429: |    (fib 1)
TRACE t4429: |    => 1
TRACE t4425: => 2
2

The latest clojure (version 1.2) has broken this mechanism by compiling the recursive call directly. If you want the old behaviour, you have to add #', meaning call it through the variable #'fib instead of linking directly.

so you need this, now: (note the #' added in two places)

(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (#'fib (- n 1)) (#'fib (- n 2)))))
(dotrace [fib] (fib 3))

This is a bit irritating. Does anyone know if there's a way of turning this inlining off globally for debugging purposes?

John Lawrence Aspden
Nice, but how do you get clojure to find 'clojure.contrib.trace? I have the clojure-contrib jar on my classpath but REPL says `user=> (use 'closure.contrib.trace)java.io.FileNotFoundException: Could not locate closure/contrib/trace__init.class or closure/contrib/trace.clj on classpath: (NO_SOURCE_FILE:0)`
LarsH
Could you be misspelling clojure as closure, or is that a typo in the comment? Can you load other clojure.contrib libraries?
John Lawrence Aspden
+10  A: 

You can also insert code to drop yourself into a REPL with all the local bindings:

(defmacro local-bindings
  "Produces a map of the names of local bindings to their values."
  []
  (let [symbols (map key @clojure.lang.Compiler/LOCAL_ENV)]
    (zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))

(declare *locals*)
(defn eval-with-locals
  "Evals a form with given locals. The locals should be a map of symbols to
values."
  [locals form]
  (binding [*locals* locals]
    (eval
     `(let ~(vec (mapcat #(list % `(*locals* '~%)) (keys locals)))
        ~form))))

(defmacro debug-repl
  "Starts a REPL with the local bindings available."
  []
  `(clojure.main/repl
    :prompt #(print "dr => ")
    :eval (partial eval-with-locals (local-bindings))))

Then to use it, insert it wherever you want the repl to start:

(defn my-function [a b c]
  (let [d (some-calc)]
    (debug-repl)))

I stick this in my user.clj so it's available in all REPL sessions.

thnetos
Credit should be given to the author: http://gist.github.com/252421
Jonathan Sampson
@Jonathan - Thanks! I couldn't remember where I originally got it.
thnetos
+1  A: 

If you use emacs/slime/swank, then try this at the REPL:

(defn factorial [n]
        (cond (< n 2) n
              (= n 23) (swank.core/break)
              :else (* n (factorial (dec n)))))

(factorial 30)

It doesn't give you a full stack trace like you'd get under LISP, but it's good for poking around.

This is the fine work of:

http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml

as was mentioned in a comment above.

John Lawrence Aspden
+1  A: 

Here's a nice macro for debugging complicated let forms: http://www.learningclojure.com/2010/09/astonishing-macro-of-narayan-singhal.html

Jouni K. Seppänen