Depuración en Clojure?

¿Cuáles son las mejores formas de depurar el código de Clojure, mientras usa el repl?

También hay dotrace, que le permite ver las entradas y salidas de las funciones seleccionadas.

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

produce la salida:

 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 

En Clojure 1.4, dotrace ha movido:

Necesitas la dependencia:

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

Y debe agregar el ^: dynamic a la definición de la función

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

Entonces Bob es una vez más tu tío:

 (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 

Tengo una pequeña macro de depuración que me parece muy útil:

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

Puede insertarlo donde desee ver lo que sucede y cuándo:

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

La CIDER de Emacs tiene un depurador de origen que puede pasar expresión por expresión dentro de un búfer de Emacs e incluso inyectar nuevos valores. Puedes leer todo sobre esto aquí . Una captura de pantalla de demostración:

Depuración de CIDER

Mi método favorito es una gran cantidad de println todo el código … println y desactivarlas es fácil gracias a la macro #_ reader (que hace que el lector lea en el siguiente formulario y luego pretende que nunca lo ha visto). O bien, podría usar una macro expandiéndose a un cuerpo transferido o nil dependiendo del valor de alguna variable especial, digamos *debug* :

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

Con a (def *debug* false) allí, esto se expandirá a nil . Con true , se expandirá al body envuelto en un do .


La respuesta aceptada a esta pregunta SO: Idiomatic Clojure para informes de progreso? es muy útil cuando se depuran operaciones de secuencia.


Luego hay algo que actualmente es incompatible con REPL de swank-clojure , pero es demasiado bueno para no mencionar: debug-repl . Puede usarlo en un REPL independiente, que es fácil de obtener, por ejemplo, con Leiningen ( lein repl ); y si está lanzando su progtwig desde la línea de comando, entonces traerá su propia REPL directamente a su terminal. La idea es que pueda soltar la macro de debug-repl en cualquier lugar que desee y hacer que aparezca su REPL cuando la ejecución del progtwig llegue a ese punto, con todos los locales en scope, etc. Un par de enlaces relevantes: Clojure debug-repl , Clojure depug-repl trucos , ¿qué hay de un debug-repl (en el grupo Clojure Google), depuración-repl en Clojars .


swank-clojure hace un trabajo adecuado para hacer que el depurador incorporado de SLIME sea útil cuando se trabaja con el código Clojure. Observe cómo los bits irrelevantes de stacktrace están atenuados por lo que es fácil encontrar el problema real en el código que se está depurando. Una cosa a tener en cuenta es que las funciones anónimas sin “tags de nombre” aparecen en la stack de fichas, básicamente sin información útil adjunta; cuando se agrega una “etiqueta de nombre”, aparece en la stack y todo vuelve a estar bien:

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

También puede insertar código para colocarse en un REPL con todas las vinculaciones locales, utilizando la debug-repl Alex Osborne :

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

Luego, para usarlo, insértelo donde desee que empiece la réplica:

 (defn my-function [abc] (let [d (some-calc)] (debug-repl))) 

Pego esto en mi user.clj así que está disponible en todas las sesiones REPL.

“mejores formas de depurar código Clojure, mientras usa el repl”

Levemente el campo izquierdo, pero “usando el REPL mismo”.

He estado escribiendo el aficionado Clojure por más de un año y no he sentido la necesidad de herramientas de depuración. Si mantiene sus funciones pequeñas, y ejecuta cada una de ellas con las entradas esperadas en el REPL y observa los resultados, entonces debería ser posible tener una idea bastante clara de cómo se comporta su código.

Encuentro que un depurador es más útil para observar STATE en una aplicación en ejecución. Clojure hace que sea fácil (¡y divertido!) Escribir en un estilo funcional con estructuras de datos inmutables (sin cambio de estado). Esto reduce masivamente la necesidad de un depurador. Una vez que sé que todos los componentes se comportan como espero (prestando especial atención a los tipos de cosas), entonces el comportamiento a gran escala rara vez es un problema.

Si usa emacs / slime / swank, intente esto en REPL:

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

No te proporciona un seguimiento de stack completo como el que obtendrías en LISP, pero es bueno para hurgar.

Este es el buen trabajo de:

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

como se mencionó en un comentario anterior.

Para IntelliJ hay un excelente plugin de Clojure llamado Cursive . Entre otras cosas, proporciona un REPL que puede ejecutar en modo de depuración y recorrer su código Clojure como lo haría para, por ejemplo, Java.

Yo diría que la respuesta de Peter Westmacott es que, en mi experiencia, simplemente ejecutar partes de mi código en el REPL es, la mayoría de las veces, una forma de depuración suficiente.

A partir de 2016, puede utilizar Debux , una biblioteca de depuración simple para Clojure / Script que funciona junto con su repl y la consola de su navegador. Puede rociar macros dbg (debug) o clog (console.log) en su código y observar fácilmente los resultados de funciones individuales, etc., impresas en su REPL y / o consola.

Del archivo Léame del proyecto:

Uso básico

Este es un ejemplo simple. El macro dbg imprime una forma original e imprime bastante el valor evaluado en la ventana REPL. Luego devuelve el valor sin interferir con la ejecución del código.

Si ajusta el código con dbg así,

(* 2 (dbg (+ 10 20))) ; => 60

lo siguiente se imprimirá en la ventana REPL.

Salida REPL:

dbg: (+ 10 20) => 30

Dbg nested

La macro dbg se puede anidar.

(dbg (* 2 (dbg (+ 10 20)))) ; => 60

Salida REPL:

 `dbg: (+ 10 20) => 30` 

dbg: (* 2 (dbg (+ 10 20))) => 60

Hugo Duncan y sus colaboradores continúan haciendo un trabajo increíble con el proyecto Ritz . Ritz-nrepl es un servidor nREPL con capacidades de depuración. Mira Hugo’s Debuggers en Clojure habla en Clojure / Conj 2012 para verlo en acción, en el video algunas de las diapositivas no son legibles por lo que es posible que quieras ver las diapositivas desde aquí .

Utilice spyscope que implemente una macro de lector personalizado para que su código de depuración sea también el código de producción https://github.com/dgrnbrg/spyscope

Aquí hay una buena macro para depurar formularios de let complicados:

 (defmacro def+ "def with binding (def+ [{:keys [abd]} {: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))) 

… y un ensayo que explica su uso .

Procedente de Java y familiarizado con Eclipse, me gusta lo que Counterclockwise (el plugin de Eclipse para el desarrollo de Clojure) tiene para ofrecer: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code

Versión de función de def-let, que convierte un let en una serie de defs. Algo de crédito va a aquí

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

Uso: necesita citar el contenido con una cita, por ejemplo

 (def-let '[a 1 b 2 c (atom 0)])