Каковы наилучшие способы отладки кода Clojure при использовании repl?
Отладка в Clojure?
Ответ 1
Там также есть точка, которая позволяет вам смотреть на входы и выходы выбранных функций.
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
выводит результат:
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
В Clojure 1.4, dotrace
переместился:
Вам нужна зависимость:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
И вам нужно добавить динамическое выражение ^: определение функции
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Затем Боб снова твой дядя:
(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
Ответ 2
У меня есть небольшой отладочный макрос, который мне очень полезен:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Вы можете вставить его туда, где хотите посмотреть, что происходит, и когда:
;; 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)
Ответ 3
Emacs CIDER получил исходный отладчик, который можно выставить в выражении внутри буфера Emacs и даже ввести новые значения. Вы можете прочитать все об этом здесь. Снимок экрана:
Ответ 4
Мой любимый метод - это либеральное разбрызгивание println
по всему коду... Простое и удобное включение и выключение благодаря макрос читателю #_
(что заставляет читателя читать следующую форму, затем притвориться, что она никогда не видела его). Или вы можете использовать макрос, расширяемый либо в переданном теле, либо в nil
в зависимости от значения некоторой специальной переменной, например *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do [email protected])))
При использовании (def *debug* false)
он будет расширяться до nil
. С true
он будет расширяться до body
, завернутого в do
.
Принятый ответ на этот вопрос SO: Idiomatic Clojure для отчетов о ходе выполнения? очень полезно при отладке операций последовательности.
Тогда есть что-то, что в настоящее время несовместимо с swank- clojure REPL, но слишком хорошо, чтобы не упоминать: debug-repl
. Вы можете использовать его в автономном REPL, который легко получить, например. с Leiningen (lein repl
); и если вы запускаете свою программу из командной строки, то она собирается привести свой собственный REPL прямо в ваш терминал. Идея состоит в том, что вы можете отказаться от макроса debug-repl
в любом месте, где бы вы ни находились, и вызвать его собственный REPL, когда выполнение программы достигает этой точки, со всеми локалями в области видимости и т.д. Несколько релевантных ссылок: Clojure debug-repl, Clojure трюки отладки, как "отладочный ответ" (в группе Clojure Google), debug-repl на Clojars.
swank- clojure выполняет адекватную работу по созданию встроенного отладчика SLIME при работе с кодом Clojure - обратите внимание на то, как неактивные биты stacktrace выделены серым цветом, поэтому легко найти актуальную проблему в код отлаживается. Следует иметь в виду, что анонимные функции без "тегов имен" отображаются в стеке, в основном, без какой-либо полезной информации; когда добавляется "тег имени", он появляется в стеке, и все снова хорошо:
(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)
^^^
Ответ 5
Вы также можете вставить код в REPL со всеми локальными привязками, используя 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))))
Затем, чтобы использовать его, вставьте его туда, куда вы хотите, чтобы начать:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Я использую это в своем user.clj, чтобы он был доступен во всех сеансах REPL.
Ответ 6
"лучшие способы отладки кода Clojure при использовании repl"
Слегка левое поле, но "используя REPL itelf".
Я пишу hobbyist Clojure более года и не испытываю большой потребности в каких-либо инструментах для отладки. Если вы сохраняете свои функции небольшими и запускаете каждый из них с ожидаемыми входами в REPL и наблюдаете за результатами, тогда должно быть достаточно четкое представление о том, как работает ваш код.
Я считаю, что отладчик наиболее полезен для наблюдения за STATE в запущенном приложении. Clojure упрощает (и весело!) писать в функциональном стиле с неизменяемыми структурами данных (без изменения состояния). Это значительно уменьшает потребность в отладчике. Как только я знаю, что все компоненты ведут себя так, как я ожидаю (обращая особое внимание на типы вещей), тогда поведение большого масштаба редко бывает проблемой.
Ответ 7
Если вы используете emacs/slime/swank, попробуйте это в REPL:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
Это не дает вам полную трассировку стека, как вы бы попали под LISP, но это хорошо для выталкивания вокруг.
Это прекрасная работа:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
как было упомянуто в комментарии выше.
Ответ 8
Для IntelliJ есть отличный плагин Clojure, называемый Cursive. Помимо прочего, он предоставляет REPL, который вы можете запустить в режиме отладки и выполнить свой код Clojure так же, как и вы, например. Java.
Я бы ответил вторым Питером Уэстмакоттом, хотя в моем опыте просто запуск фрагментов моего кода в REPL в большинстве случаев является достаточной формой отладки.
Ответ 9
Hugo Duncan и соавторы продолжают делать удивительную работу с проектом ritz. Ritz-nrepl - это сервер nREPL с возможностями отладки. Посмотрите Hugo Отладчики в Clojure поговорите в Clojure/Conj 2012, чтобы увидеть его в действии, в видео некоторые слайды не читаются, поэтому вы можете захотеть для просмотра слайдов здесь.
Ответ 10
С 2016 года вы можете использовать Debux, простую библиотеку отладки для Clojure/Script, которая работает в сочетании с вашим а также консоль вашего браузера. Вы можете посыпать макросы dbg
(debug) или clog
(console.log) в свой код и легко наблюдать результаты отдельных функций и т.д., Печататься на REPL и/или консоли.
Из проекта Readme:
Основное использование
Это простой пример. Макрос dbg печатает оригинальную форму и pretty-prints вычисленное значение в окне REPL. Затем он возвращается значение, не мешая выполнению кода.
Если вы переносите код с помощью dbg, как это,
(* 2 (dbg (+ 10 20))) ; => 60
в REPL.
Выход REPL:
dbg: (+ 10 20) => 30
Вложенный dbg
Макрос dbg может быть вложен.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
Выход REPL:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Ответ 11
Вот хороший макрос для отладки сложных форм 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)))
Ответ 12
Используйте spyscope, который реализует пользовательский макрос читателя, чтобы ваш код отладки был также производственным кодом https://github.com/dgrnbrg/spyscope
Ответ 13
Начиная с Java и знакомого с Eclipse, мне нравится, что Counterclockwise (плагин Eclipse для разработки Clojure) может предложить: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
Ответ 14
Функциональная версия def-let, которая превращает let в ряд defs. Некоторые кредиты относятся к здесь
(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)))))
Использование: необходимо процитировать содержимое с помощью цитаты, например.
(def-let '[a 1 b 2 c (atom 0)])