Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging in Clojure? [closed]

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

In Clojure 1.4, dotrace has moved:

You need the dependency:

[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)

And you need to add the ^:dynamic to the function definition

(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))

Then Bob is once again your uncle:

(clojure.tools.trace/dotrace [fib] (fib 3))

TRACE t4328: (fib 3)
TRACE t4329: | (fib 2)
TRACE t4330: | | (fib 1)
TRACE t4330: | | => 1
TRACE t4331: | | (fib 0)
TRACE t4331: | | => 0
TRACE t4329: | => 1
TRACE t4332: | (fib 1)
TRACE t4332: | => 1
TRACE t4328: => 2

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)

Emacs's CIDER got a source debugger that you can step expression by expression inside an Emacs buffer and even inject new values. You can read all about it here. A demo screenshot:

CIDER debug


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)
                   ^^^

You can also insert code to drop yourself into a REPL with all the local bindings, using Alex Osborne's debug-repl:

(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.


"best ways to Debug Clojure code, while using the repl"

Slightly left-field, but 'using the REPL iteself'.

I've been writing hobbyist Clojure for over a year and haven't felt a great need for any debugging tools. If you keep your functions small, and run each one with expected inputs at the REPL and observe the results then it should be possible to have a pretty clear picture of how your code is behaving.

I find a debugger is most useful for observing STATE in a running application. Clojure makes it easy (and fun!) to write in a functional style with immutable data structures (no changing state). This massively reduces the need for a debugger. Once I know that all the components behave as I expect (paying particular attention to the types of things) then the large scale behaviour is rarely a problem.


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.