web-dev-qa-db-fra.com

Déboguer dans Clojure?

Quels sont les meilleurs moyens de déboguer du code Clojure, tout en utilisant le repl?

223
Arun R

Il y a aussi la fonction dotrace, qui vous permet d'examiner les entrées et les sorties des fonctions sélectionnées.

(use 'clojure.contrib.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 été déplacé:

Vous avez besoin de la dépendance:

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

Et vous devez ajouter le ^: dynamique à 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
155
John Lawrence Aspden

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)
100
John Lawrence Aspden

Le CIDER d'Emacs a obtenu un débogueur source que vous pouvez déplacer expression par expression dans un tampon Emacs et même injecter de nouvelles valeurs. Vous pouvez lire tout à ce sujet ici . Une capture d'écran de démonstration:

CIDER debug

63
Amumu

Ma méthode préférée est un saupoudrage généreux de printlns sur tout le code ... Il est facile de les allumer et de les éteindre grâce au #_ macro de lecteur (qui lit le lecteur sous la forme suivante, puis fait comme si de rien n'était). Vous pouvez également utiliser une macro développant soit un corps transmis, soit nil, en fonction de la valeur d’une variable spéciale, par exemple *debug*:

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

Avec un (def *debug* false) dedans, cela s’étendra à nil. Avec true, il sera étendu à body enveloppé dans un do.


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


Ensuite, il y a quelque chose qui est actuellement incompatible avec la REPL de swank-clojure , mais il est trop bon de ne pas en mentionner: debug-repl _ . Vous pouvez l’utiliser dans un REPL autonome, facile à obtenir, par exemple. avec Leiningen (lein repl); et si vous lancez votre programme à partir de la ligne de commande, il apportera le sien REPL directement dans votre terminal. L'idée est que vous pouvez supprimer le debug-repl macro dans n’importe où vous le voulez et donnez-lui le sien REPL lorsque l’exécution du programme arrive à ce point, avec toutes les sections locales dans son étendue, etc. Quelques liens pertinents: Le Clojure debug-repl , astuces Clojure debug-repl , comment effectuer 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 trace de pile sont grisés afin qu'il soit facile de trouver le problème réel dans le code en cours de débogage. Une chose à garder à l'esprit est que des fonctions anonymes sans "tags de nom" apparaissent dans la pile, sans informations utiles les concernant; quand une "étiquette de nom" est ajoutée, elle apparaît dans le stacktrace et tout va 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)
                   ^^^
46
Michał Marczyk

Vous pouvez également insérer du code pour vous déposer dans un REPL avec toutes les liaisons locales, à l'aide de le script Alex Osborne 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))))

Ensuite, pour l'utiliser, insérez-le où vous voulez que la répl commence:

(defn my-function [a b c]
  (let [d (some-calc)]
    (debug-repl)))

Je la colle dans mon user.clj afin qu’elle soit disponible dans toutes les REPL sessions.

36
thnetos

"meilleurs moyens de déboguer le code Clojure, tout en utilisant le repl"

Légèrement à gauche du champ, mais 'en utilisant le REPL iteself').

Cela fait plus d'un an que j'écris Clojure, un amateur, et je n'ai pas vraiment besoin d'outils de débogage. Si vous limitez vos fonctions et que vous exécutez chacune d’entre elles avec les entrées attendues au niveau de REPL) et que vous 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 l'état dans une application en cours d'exécution. Clojure facilite l'écriture (et l'amusement!) Dans un style fonctionnel avec des structures de données immuables (sans changement d'état). Cela réduit considérablement le besoin d'un débogueur. Une fois que je sais que tous les composants se comportent comme je l’attendais (en accordant une attention particulière au type de choses), le comportement à grande échelle pose rarement problème.

14
Peter Westmacott

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

J'approcherais la réponse de Peter Westmacott, bien que, dans mon expérience, le simple fait d'exécuter des parties de mon code dans le REPL soit la plupart du temps une forme suffisante de débogage.

9
dskrvk

Si vous utilisez emacs/slime/swank, essayez ceci au niveau du 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 de pile complète comme vous le feriez avec LISP, mais c'est bon pour fouiller.

C'est le beau travail de:

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

comme mentionné dans un commentaire ci-dessus.

9

À partir de 2016, vous pouvez utiliser Debux , une simple bibliothèque de débogage pour Clojure/Script qui fonctionne conjointement avec votre repl et la console de 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és sur votre REPL = et/ou console.

Depuis le projet Readme :

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). Elle renvoie ensuite 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

ce qui suit sera imprimé dans la fenêtre REPL.

Sortie REPL:

dbg: (+ 10 20) => 30

Dbg imbriqué

La macro dbg peut être imbriquée.

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

Sortie REPL:

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

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

6
Mallory-Erik

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

5
Rodrigo Taboada

Voici une belle macro pour le débogage de formes compliquées 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)))

... et n essai expliquant son utilisation .

2
Jouni K. Seppänen

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

2
myguidingstar

Venant de Java et connaissant Eclipse, j'aime ce que propose Counterclockwise (le plugin Eclipse pour le développement Clojure): http://doc.ccw-ide.org/ documentation.html # _debug_clojure_code

1
yotsov