Depuração no Clojure?

Quais são as melhores maneiras de depurar o código Clojure, ao usar a replicação?

Há também o dotrace, que permite observar as inputs e saídas de funções selecionadas.

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

produz a saída:

 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 

No Clojure 1.4, o dotrace foi movido:

Você precisa da dependência:

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

E você precisa adicionar o ^: dynamic à definição da function

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

Então Bob é mais uma vez seu tio:

 (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 

Eu tenho uma pequena macro de debugging que acho muito útil:

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

Você pode inseri-lo onde quiser para assistir o que está acontecendo e quando:

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

O CIDER do Emacs tem um depurador de código-fonte que você pode passar a expressão por expressão dentro de um buffer do Emacs e até mesmo injetar novos valores. Você pode ler tudo sobre isso aqui . Uma captura de canvas de demonstração:

Depuração de CIDER

O meu método favorito é o espalhamento liberal de todo o código … É fácil #_ los e desligá-los graças à macro #_ reader (que faz o leitor ler da seguinte forma, depois fingir que nunca o viu). Ou você poderia usar uma macro expandindo para um corpo passado ou nil dependendo do valor de alguma variável especial, digamos *debug* :

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

Com um (def *debug* false) lá, isso se expandirá para nil . Com a true , ele se expandirá para o body envolto em um do .


A resposta aceita para esta pergunta SO: Clormaure idiomático para relatórios de progresso? é muito útil ao depurar operações de sequência.


Então há algo que é atualmente incompatível com o REPL do swank-clojure , mas é bom demais para não mencionar: debug-repl . Você pode usá-lo em um REPL autônomo, que é fácil de obter, por exemplo, com Leiningen ( lein repl ); e se você estiver lançando seu programa a partir da linha de comando, ele trará seu próprio REPL para cima no seu terminal. A idéia é que você pode soltar a macro debug-repl em qualquer lugar que desejar e fazer com que ela apareça no REPL quando a execução do programa atingir esse ponto, com todos os locais no escopo, etc. Alguns links relevantes: The Clojure debug-repl , Truques de replicação de debugging do Clojure , como depurar uma replicação (no grupo Clojure do Google), debugging-repl no Clojars .


O swank-clojure faz um trabalho adequado ao tornar o depurador integrado do SLIME útil quando se trabalha com o código Clojure – note como os bits irrelevantes do stacktrace estão acinzentados para que seja fácil encontrar o problema real no código que está sendo depurado. Uma coisa a ter em mente é que funções anônimas sem “tags de nome” aparecem no stacktrace com basicamente nenhuma informação útil anexada a elas; quando uma “tag de nome” é adicionada, ela aparece no stacktrace e está tudo bem novamente:

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

Você também pode inserir o código para se soltar em um REPL com todas as ligações locais, usando o 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)))) 

Em seguida, para usá-lo, insira-o onde quiser que a repl seja iniciada:

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

Eu coloco isso no meu user.clj para que esteja disponível em todas as sessões do REPL.

“melhores maneiras de depurar o código Clojure, ao usar a repl”

Ligeiramente à esquerda, mas “usando o próprio REPL”.

Eu tenho escrito o hobbyist Clojure por mais de um ano e não senti uma grande necessidade de ferramentas de debugging. Se você mantiver suas funções pequenas e executar cada uma com as inputs esperadas no REPL e observar os resultados, deverá ser possível ter uma visão clara de como seu código está se comportando.

Eu acho um depurador é mais útil para observar o estado em um aplicativo em execução. O Clojure torna mais fácil (e divertido!) Escrever em um estilo funcional com estruturas de dados imutáveis ​​(sem mudança de estado). Isso reduz enormemente a necessidade de um depurador. Uma vez que eu sei que todos os componentes se comportam como eu esperava (com especial atenção para os tipos de coisas), então o comportamento em larga escala raramente é um problema.

Se você usa emacs / slime / swank, tente isso no REPL:

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

Ele não te dá um rastreio de pilha completo como você ficaria sob LISP, mas é bom para bisbilhotar.

Este é o bom trabalho de:

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

como foi mencionado em um comentário acima.

Para o IntelliJ, há um excelente plugin do Clojure chamado Cursive . Entre outras coisas, ele fornece um REPL que você pode executar no modo de debugging e percorrer seu código Clojure como faria para, por exemplo, Java.

Eu questionaria a resposta de Peter Westmacott, na medida em que, na minha experiência, apenas executar partes do meu código no REPL é, na maioria das vezes, uma forma suficiente de debugging.

A partir de 2016 você pode usar o Debux , uma biblioteca de debugging simples para o Clojure / Script que funciona em conjunto com a sua replicação, bem como com o console do seu navegador. Você pode espalhar as macros dbg (debug) ou clog (console.log) em seu código e observar facilmente os resultados de funções individuais, etc, impressas no seu REPL e / ou console.

Do Leiame do projeto:

Uso básico

Este é um exemplo simples. A macro dbg imprime um formulário original e imprime na canvas o valor avaliado na janela REPL. Em seguida, retorna o valor sem interferir na execução do código.

Se você quebrar o código com o dbg,

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

o seguinte será impresso na janela REPL.

Saída REPL:

dbg: (+ 10 20) => 30

Dbg nested

A macro dbg pode ser aninhada.

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

Saída REPL:

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

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

Hugo Duncan e colaboradores continuam fazendo um trabalho incrível com o projeto Ritz . O Ritz-nrepl é um servidor nREPL com resources de debugging. Assista os depuradores de Hugo em Clojure falar no Clojure / Conj 2012 para vê-lo em ação, no vídeo alguns dos slides não são legíveis, então você pode querer ver os slides daqui .

Use o spyscope que implementa uma macro de leitura personalizada para que seu código de debugging também seja código de produção https://github.com/dgrnbrg/spyscope

Aqui está uma boa macro para debugging de formulários 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))) 

… e um ensaio explicando seu uso .

Vindo do Java e estando familiarizado com o Eclipse, eu gosto do que o Counterclockwise (o plugin do Eclipse para o desenvolvimento do Clojure) tem a oferecer: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code

Função version of def-let, que transforma um let em uma série de defs. Algum crédito vai para aqui

 (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: precisa citar o conteúdo com uma cotação, por exemplo

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