Debugging in Clojure? [closed]
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this questionWhat are best ways to Debug Clojure code, while using the repl?
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:
My favourite method is a liberal sprinkling of println
s 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.
For IntelliJ there's an excellent Clojure plugin called Cursive. Among other things, it provides a REPL which you can run in debug mode and step through your Clojure code just like you would for e.g. Java.
I would second Peter Westmacott's answer though in that in my experience just running pieces of my code in the REPL is most of the time a sufficient form of debugging.
As of 2016 you can use Debux, a simple debugging library for Clojure/Script that works in conjunction with your repl as well as your browser's console. You can sprinkle dbg
(debug) or clog
(console.log) macros in your code and easily observe results of individual functions, etc, printed to your REPL and/or console.
From the project's Readme:
Basic usage
This is a simple example. The macro dbg prints an original form and pretty-prints the evaluated value on the REPL window. Then it returns the value without interfering with the code execution.
If you wrap the code with dbg like this,
(* 2 (dbg (+ 10 20))) ; => 60
the following will be printed in the REPL window.
REPL output:
dbg: (+ 10 20) => 30
Nested dbg
The dbg macro can be nested.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
REPL output:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan and collaborators continue to do amazing work with the ritz project. Ritz-nrepl is a nREPL server with debug capabilities. Watch Hugo's Debuggers in Clojure talk at Clojure/Conj 2012 to see it in action, in the video some of the slides aren't readable so you may want to view the slides from here.
Use spyscope which implement a custom reader macro so that your debug code is also production code https://github.com/dgrnbrg/spyscope
Coming from Java and being familiar with Eclipse, I like what Counterclockwise (the Eclipse plugin for Clojure development) has to offer: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
Here's a nice macro for debugging complicated let
forms:
(defmacro def+
"def with binding (def+ [{:keys [a b d]} {:a 1 :b 2 :d 3}])"
[bindings]
(let [let-expr (macroexpand `(let ~bindings))
vars (filter #(not (.contains (str %) "__"))
(map first (partition 2 (second let-expr))))
def-vars (map (fn [v] `(def ~v ~v)) vars)]
(concat let-expr def-vars)))
...and an essay explaining its use.
Function version of def-let, which turns a let into a series of defs. Some credit goes to here
(defn def-let [aVec]
(if-not (even? (count aVec))
aVec
(let [aKey (atom "")
counter (atom 0)]
(doseq [item aVec]
(if (even? @counter)
(reset! aKey item)
(intern *ns* (symbol @aKey) (eval item)))
; (prn item)
(swap! counter inc)))))
Usage: Needs to quote the content with a quotation, e.g.
(def-let '[a 1 b 2 c (atom 0)])
精彩评论