debugging ¿Depuración en Clojure?





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

Puedes insertarlo donde quieras para ver qué está pasando 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)
debugging clojure

¿Cuáles son las mejores maneras de depurar el código de Clojure, al usar la respuesta?




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 el siguiente formulario y luego finja que nunca lo ha visto). O puede usar una macro que se expanda a un cuerpo pasado o nil dependiendo del valor de alguna variable especial, digamos *debug* :

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

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

La respuesta aceptada a esta pregunta de SO: ¿ Clojure idiomático para informes de progreso? Es muy útil al depurar operaciones de secuencia.

Luego hay algo que actualmente es incompatible con el REPL de swank-clojure , pero es demasiado bueno para no mencionarlo: debug-repl . Puede usarlo en un REPL autónomo, que es fácil de obtener, por ejemplo, con Leiningen ( lein repl ); y si está iniciando su programa desde la línea de comandos, entonces su propio REPL se activará en su terminal. La idea es que puede colocar la macro debug-repl en cualquier lugar que desee y hacer que muestre su propio REPL cuando la ejecución del programa llegue a ese punto, con todos los locales en el ámbito, etc. Un par de enlaces relevantes: The Clojure debug-repl , Trucos de debug-repl de Clojure , cómo sobre una debug-repl (en el grupo de Google Clojure), debug-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 de Clojure. Observe cómo los bits irrelevantes del seguimiento de la pila están en gris, por lo que es fácil encontrar el problema real en el código que se está depurando. Una cosa que se debe tener en cuenta es que las funciones anónimas sin "etiquetas de nombre" aparecen en el seguimiento de pila y, básicamente, no tienen información útil adjunta; cuando se agrega una "etiqueta de nombre", aparece en el seguimiento de 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)
                   ^^^



"las mejores formas de depurar el código de Clojure, mientras se usa la respuesta"

Un poco de campo izquierdo, pero 'usando el REPL por sí mismo'.

He estado escribiendo Clojure aficionado durante más de un año y no he sentido una gran necesidad de herramientas de depuración. Si mantiene sus funciones pequeñas y ejecuta cada una 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 está comportando su código.

Me parece 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 lo espero (prestando especial atención a los tipos de cosas), el comportamiento a gran escala rara vez es un problema.




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

Sin embargo, me gustaría respaldar la respuesta de Peter Westmacott, ya que, en mi experiencia, solo ejecutar partes de mi código en el REPL es la mayoría de las veces una forma suficiente de depuración.




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. Mire a los Debuggers de Hugo en Clojure hablar en Clojure / Conj 2012 para verlo en acción, en el video algunas de las diapositivas no se pueden leer, por lo que es posible que desee ver las diapositivas desde here .




Here's una buena macro para la depuración de formularios complicados de let :

(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 explicando su uso .




Versión de la función de def-let, que convierte un let en una serie de defs. Algún crédito va hasta 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 cotizar el contenido con una cotización, por ej.

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



Related