[Debugging] Depuración en Clojure?



Answers

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

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




Versión de función de def-let, que convierte un let en una serie de defs. Algo de crédito va a 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)))))

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

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



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




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




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.




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 programa 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 programa llegue a ese punto, con todos los locales en alcance, 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 "etiquetas de nombre" aparecen en la pila de fichas, básicamente sin información útil adjunta; cuando se agrega una "etiqueta de nombre", aparece en la pila 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)
                   ^^^



Here's una buena macro para depurar formularios de let complicados:

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

... y un ensayo que explica su uso .




Links