Déboguer dans Clojure?

Quelles sont les meilleures façons de déboguer le code Clojure, tout en utilisant le fichier repl?

Il y a aussi le dotrace, qui vous permet de regarder les entrées et les sorties des fonctions sélectionnées.

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

produit la sortie:

 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 

Dans Clojure 1.4, dotrace a déplacé:

Vous avez besoin de la dépendance:

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

Et vous devez append le ^: dynamic à la définition de la fonction

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

Alors Bob est encore une fois ton oncle:

 (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 

J’ai une petite macro de débogage que je trouve très utile:

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

Vous pouvez l’insérer où vous voulez regarder ce qui se passe et quand:

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

Le CIDER d’Emacs a obtenu un débogueur source que vous pouvez exécuter par expression dans une mémoire tampon d’Emacs et même injecter de nouvelles valeurs. Vous pouvez tout lire ici . Une capture d’écran de démonstration:

Débogage CIDER

Ma méthode préférée est un saupoudrage généreux d’ println partout dans le code … Il est facile de les #_ et de les désactiver grâce à la macro #_ reader (qui fait lire au lecteur sous la forme suivante, puis fait semblant de ne jamais l’avoir vu). Ou vous pouvez utiliser une macro qui s’étend soit à un corps passé, soit à une valeur nil fonction de la valeur de certaines variables spéciales, par exemple *debug* :

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

Avec un (def *debug* false) , cela va s’étendre à nil . Avec true , il va s’étendre au body enveloppé dans un do .


La réponse acceptée à cette question SO: Clojure idiomatique pour des rapports d’avancement? est très utile lors du débogage des opérations de séquence.


Ensuite, il y a quelque chose qui est actuellement incompatible avec la REPL de swank-clojure , mais qui est trop beau pour ne pas mentionner: debug-repl . Vous pouvez l’utiliser dans une REPL autonome, ce qui est facile à obtenir, par exemple avec Leiningen ( lein repl ); et si vous lancez votre programme à partir de la ligne de commande, il va apporter sa propre REPL dans votre terminal. L’idée est que vous pouvez déposer la macro debug-repl à n’importe quel endroit et la faire afficher sa propre REPL lorsque l’exécution du programme atteint ce point, avec toutes les sections locales concernées. Quelques liens pertinents: Le debug-repl de Clojure Clojure debug-repl astuces , comment un debug-repl (sur le groupe Clojure Google), debug-repl sur Clojars .


swank-clojure fait un travail adéquat pour rendre le débogueur intégré de SLIME utile lorsque vous travaillez avec du code Clojure – notez que les bits non pertinents de la stacktrace sont grisés, il est donc facile de trouver le problème dans le code en cours de débogage. Une chose à garder à l’esprit est que les fonctions anonymes sans “tags de nom” apparaissent dans le stacktrace sans aucune information utile qui leur soit attachée; Quand une “étiquette de nom” est ajoutée, elle apparaît dans la stack et tout va bien:

 (fn [& args] ...) vs. (fn tag [& args] ...) example stacktrace ensortinges: 1: user$eval__3130$fn__3131.invoke(NO_SOURCE_FILE:1) vs. ^^ 1: user$eval__3138$tag__3139.invoke(NO_SOURCE_FILE:1) ^^^ 

Vous pouvez également insérer du code pour vous déposer dans une REPL avec toutes les liaisons locales, en utilisant le 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.Comstackr/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)))) 

Ensuite, pour l’utiliser, insérez-le où vous voulez que le réplicateur démarre:

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

Je tiens ceci dans mon user.clj donc il est disponible dans toutes les sessions de REPL.

“les meilleures façons de déboguer le code Clojure, tout en utilisant le repl”

Champ légèrement gauche, mais “utiliser le fichier REPL lui-même”.

Cela fait plus d’un an que j’écris le loisir amateur Clojure et je n’ai pas eu besoin d’outils de débogage. Si vous gardez vos fonctions réduites, et exécutez chacune d’elles avec les entrées attendues au REPL et observez les résultats, il devrait être possible d’avoir une image assez claire du comportement de votre code.

Je trouve qu’un débogueur est le plus utile pour observer STATE dans une application en cours d’exécution. Clojure facilite (et amusant!) L’écriture dans un style fonctionnel avec des structures de données immuables (pas d’état changeant). Cela réduit considérablement le besoin d’un débogueur. Une fois que je sais que tous les composants se comportent comme prévu (en accordant une attention particulière aux types de choses), le comportement à grande échelle pose rarement problème.

Si vous utilisez emacs / slime / swank, essayez ceci dans le REPL:

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

Cela ne vous donne pas une trace complète de la stack comme vous le feriez sous LISP, mais c'est bon pour faire le tour.

C'est le bon travail de:

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

comme mentionné dans un commentaire ci-dessus.

Pour IntelliJ, il existe un excellent plugin Clojure appelé Cursive . Entre autres choses, il fournit une REPL que vous pouvez exécuter en mode debug et parcourir votre code Clojure comme vous le feriez par exemple pour Java.

Je voudrais seconder la réponse de Peter Westmacott, mais dans mon expérience, la gestion de certaines parties de mon code dans le REPL est la plupart du temps une forme de débogage suffisante.

Depuis 2016, vous pouvez utiliser Debux , une bibliothèque de débogage simple pour Clojure / Script qui fonctionne conjointement avec votre console repl et votre navigateur. Vous pouvez saupoudrer des macros dbg (debug) ou clog (console.log) dans votre code et observer facilement les résultats de fonctions individuelles, etc., imprimées sur votre REPL et / ou votre console.

Du fichier Readme du projet:

Utilisation de base

Ceci est un exemple simple. La macro dbg imprime un formulaire original et imprime la valeur évaluée dans la fenêtre REPL. Ensuite, il renvoie la valeur sans interférer avec l’exécution du code.

Si vous enveloppez le code avec dbg comme ceci,

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

les éléments suivants seront imprimés dans la fenêtre REPL.

Sortie REPL:

dbg: (+ 10 20) => 30

Dbg nested

La macro dbg peut être nestede.

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

Sortie REPL:

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

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

Hugo Duncan et ses collaborateurs continuent de faire un travail extraordinaire avec le projet Ritz . Ritz-nrepl est un serveur nREPL avec des capacités de débogage. Regardez les débogueurs de Hugo dans Clojure parler à Clojure / Conj 2012 pour le voir en action, dans la vidéo, certaines des diapositives ne sont pas lisibles.

Utilisez spyscope qui implémente une macro de lecteur personnalisée afin que votre code de débogage soit également le code de production https://github.com/dgrnbrg/spyscope

Voici une belle macro pour le débogage des formulaires de let compliqués:

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

… et un essai expliquant son utilisation .

Venant de Java et familiarisé avec Eclipse, j’aime ce que Counterclockwise (le plugin Eclipse pour le développement de Clojure) a à offrir: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code

Version de fonction de def-let, qui transforme un let dans une série de defs. Un peu de crédit va ici

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

Utilisation: nécessite de citer le contenu avec une citation, par exemple

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