Debugging di Clojure? [Tutup]

227

Apa cara terbaik untuk kode Debug Clojure, saat menggunakan repl?

Arun R
sumber
Sebagai tambahan pada jawaban di bawah ini, lihat 'Alat dan teknik debugging' dalam panduan REPL
Valentin Waeselynck

Jawaban:

158

Ada juga dotrace, yang memungkinkan Anda untuk melihat input dan output fungsi yang dipilih.

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

menghasilkan output:

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

Di Clojure 1.4, dotracetelah pindah:

Anda membutuhkan ketergantungan:

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

Dan Anda perlu menambahkan ^: dinamis ke definisi fungsi

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

Kemudian Bob sekali lagi pamanmu:

(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
John Lawrence Aspden
sumber
2
Bagus, tetapi bagaimana Anda mendapatkan clojure untuk menemukan 'clojure.contrib.trace? Saya memiliki stoples clojure-contrib di classpath saya tetapi REPL mengatakanuser=> (use 'closure.contrib.trace) java.io.FileNotFoundException: Could not locate closure/contrib/trace__init.class or closure/contrib/trace.clj on classpath: (NO_SOURCE_FILE:0)
LarsH
2
Mungkinkah Anda salah mengeja clojure sebagai penutup, atau apakah itu salah ketik dalam komentar? Bisakah Anda memuat pustaka clojure.contrib lainnya?
John Lawrence Aspden
12
Pada 1.3 ini telah pindah ke clojure.tools.trace ( github.com/clojure/tools.trace )
George
4
Jika Anda mendapatkan: "IllegalStateException Tidak dapat secara dinamis mengikat var non-dinamis" lihat di sini: stackoverflow.com/questions/8875353/…
Cornelius
2
Apakah ini berfungsi di rilis 1.5 juga? Saya belajar Clojure dengan koan clojure, tetapi belum bisa mendapatkan dotrace untuk bekerja.
nha
100

Saya memiliki makro debugging kecil yang menurut saya sangat berguna:

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

Anda dapat memasukkannya di mana pun Anda ingin menonton apa yang terjadi dan kapan:

;; 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)
John Lawrence Aspden
sumber
Sangat mirip clojure.tools.trace/trace.
Zaz
4
Bahkan lebih baik: Spyscope .
Zaz
@ Zaz, saya setuju sepenuhnya. Spyscope luar biasa! Bahkan lebih baik daripada debugger. Tentunya untuk mengetik.
J Atkin
66

CIDER Emacs mendapatkan sumber debugger yang dapat Anda langkah ekspresi dengan ekspresi di dalam buffer Emacs dan bahkan menyuntikkan nilai baru. Anda dapat membaca semuanya di sini . Tangkapan layar demo:

Debug CIDER

Amumu
sumber
46

Metode favorit saya adalah taburan liberal printlndi seluruh kode ... Mengaktifkan dan menonaktifkannya mudah berkat #_makro pembaca (yang membuat pembaca membaca dalam bentuk berikut, lalu berpura-pura tidak pernah melihatnya). Atau Anda bisa menggunakan ekspansi makro baik ke badan yang lewat atau niltergantung pada nilai beberapa variabel khusus, katakan *debug*:

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

Dengan (def *debug* false)di sana, ini akan diperluas ke nil. Dengan true, itu akan berkembang menjadi bodyterbungkus a do.


Jawaban yang diterima untuk pertanyaan SO ini: Clojure Idiomatik untuk pelaporan kemajuan? sangat membantu ketika men-debug operasi urutan.


Lalu ada sesuatu yang saat ini tidak sesuai dengan berlagak-clojure 's REPL, tapi terlalu baik untuk tidak menyebutkan: debug-repl. Anda dapat menggunakannya dalam REPL mandiri, yang mudah didapat misalnya dengan Leiningen ( lein repl); dan jika Anda meluncurkan program Anda dari baris perintah, maka itu akan membawa REPL sendiri tepat di terminal Anda. Idenya adalah bahwa Anda dapat menjatuhkan debug-replmakro di mana saja Anda suka dan memunculkan REPL sendiri ketika eksekusi program mencapai titik itu, dengan semua penduduk setempat dalam ruang lingkup dll. Beberapa tautan yang relevan: Clojure debug-repl , Clojure debug trik -repl , bagaimana dengan debug-repl (pada grup Google Clojure), debug-repl pada Clojars .


swank-clojure melakukan pekerjaan yang memadai untuk menjadikan debugger bawaan SLIME bermanfaat saat bekerja dengan kode Clojure - perhatikan bagaimana bit yang tidak relevan dari stacktrace diklik sehingga mudah untuk menemukan masalah sebenarnya dalam kode yang sedang di-debug. Satu hal yang perlu diingat adalah bahwa fungsi anonim tanpa "tag nama" muncul di stacktrace dengan dasarnya tidak ada informasi berguna yang melekat padanya; ketika "tag nama" ditambahkan, itu muncul di stacktrace dan semuanya baik-baik saja:

(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)
                   ^^^
Michał Marczyk
sumber
5
Sebenarnya ada versi debug-repl yang berfungsi dengan swank sekarang: hugoduncan.org/post/2010/… (Spoiler lansiran: mengagumkan)
1
Benar, dan ada baiknya memiliki tautan di sini, terima kasih! Setuju pada mengagumkan. :-)
Michał Marczyk
Jika ini gaya Anda, Anda mungkin menyukai perpustakaan debux yang disebutkan dalam respons selanjutnya. github.com/philoskim/debux
Mallory-Erik
@ Mallory-Erik Terima kasih, saya akan memeriksanya!
Michał Marczyk
37

Anda juga dapat memasukkan kode untuk menjatuhkan diri ke REPL dengan semua binding lokal, menggunakan Alex Osbornedebug-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))))

Kemudian untuk menggunakannya, masukkan di mana pun Anda ingin memulai:

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

Saya menempel ini di user.clj saya sehingga ini tersedia di semua sesi REPL.

thnetos
sumber
16

"cara terbaik untuk kode Debug Clojure, saat menggunakan repl"

Bidang kiri sedikit, tetapi 'menggunakan REPL itu sendiri'.

Saya telah menulis hobi Clojure selama lebih dari setahun dan belum merasakan kebutuhan yang besar untuk alat debugging. Jika Anda menjaga fungsi Anda kecil, dan menjalankan masing-masing dengan input yang diharapkan di REPL dan mengamati hasilnya maka itu mungkin untuk memiliki gambaran yang cukup jelas tentang bagaimana kode Anda berperilaku.

Saya menemukan debugger paling berguna untuk mengamati STATE dalam aplikasi yang sedang berjalan. Clojure membuatnya mudah (dan menyenangkan!) Untuk menulis dalam gaya fungsional dengan struktur data yang tidak berubah (tidak ada perubahan kondisi). Ini secara besar-besaran mengurangi kebutuhan akan debugger. Setelah saya tahu bahwa semua komponen berperilaku seperti yang saya harapkan (memberikan perhatian khusus pada jenis-jenis hal) maka perilaku skala besar jarang menjadi masalah.

Peter Westmacott
sumber
Ini sebagian besar benar tetapi ketika Anda memiliki rekursi di beberapa fungsi misalnya itu tidak mudah.
John
9

Jika Anda menggunakan emacs / slime / swank, maka coba ini di REPL:

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

(factorial 30)

Itu tidak memberi Anda jejak tumpukan penuh seperti yang Anda dapatkan di bawah LISP, tapi itu bagus untuk mencari-cari.

Ini adalah karya bagus dari:

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

seperti yang disebutkan dalam komentar di atas.

John Lawrence Aspden
sumber
9

Untuk IntelliJ ada plugin Clojure yang sangat baik yang disebut Cursive . Antara lain, ia menyediakan REPL yang dapat Anda jalankan dalam mode debug dan melangkah melalui kode Clojure Anda seperti yang Anda lakukan untuk Java misalnya.

Saya akan menjawab kedua jawaban Peter Westmacott karena dalam pengalaman saya hanya menjalankan potongan kode saya di REPL sebagian besar waktu merupakan bentuk debugging yang cukup.

dskrvk
sumber
Saya menggunakan La Clojure dengan sukses, tetapi tampaknya sekarat demi kursif sekarang github.com/JetBrains/la-clojure/blob/master/README.md
leeor
Tapi bagaimana cara debug Leiningen, ini menunjukkan:Error running 'ring server': Trampoline must be enabled for debugging
Gank
Itu tampaknya khusus untuk ringatau lein- mungkin layak posting pertanyaan terpisah?
dskrvk
6

Pada 2016 Anda dapat menggunakan Debux , pustaka debugging sederhana untuk Clojure / Script yang bekerja bersama dengan repl Anda serta konsol browser Anda. Anda dapat menaburkan dbg(debug) atau clog(console.log) makro dalam kode Anda dan dengan mudah mengamati hasil fungsi individu, dll, dicetak ke REPL Anda dan / atau konsol.

Dari Readme proyek :

Penggunaan dasar

Ini adalah contoh sederhana. Makro dbg mencetak bentuk asli dan mencetak nilai yang dievaluasi pada jendela REPL. Kemudian mengembalikan nilai tanpa mengganggu pelaksanaan kode.

Jika Anda membungkus kode dengan dbg seperti ini,

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

berikut ini akan dicetak di jendela REPL.

Output REPL:

dbg: (+ 10 20) => 30

Dbg bersarang

Makro dbg dapat disarangkan.

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

Output REPL:

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

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

Mallory-Erik
sumber
5

Hugo Duncan dan kolaborator terus melakukan pekerjaan luar biasa dengan proyek ritz . Ritz-nrepl adalah server nREPL dengan kemampuan debug. Tonton Hugo Debuggers di Clojure berbicara di Clojure / Conj 2012 untuk melihatnya beraksi, dalam video beberapa slide tidak dapat dibaca sehingga Anda mungkin ingin melihat slide dari sini .

Rodrigo Taboada
sumber
1

Berasal dari Jawa dan terbiasa dengan Eclipse, saya menyukai apa yang berlawanan dengan berlawanan (plugin Eclipse untuk pengembangan Clojure) tawarkan: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code

yotsov
sumber
tidak ada dukungan breakpoint yang tepat. Thread sulit untuk dibunuh.
CodeFarmer
1

Berikut ini makro bagus untuk debugging letformulir yang rumit :

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

... dan esai yang menjelaskan penggunaannya .

Jouni K. Seppänen
sumber
-4

Versi fungsi dari def-let, yang mengubah let menjadi serangkaian def. Beberapa kredit masuk ke sini

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

Penggunaan: Perlu mengutip konten dengan kutipan, misalnya

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