понедельник, 2 апреля 2012 г.

Отладка в Clojure

В данной статье хочу рассказать о некоторых методах отладки в clojure.

Метод 1. Использование точек останова в slime.

При использовании emacs появляется возможность устранавливать точки останова(breakpoints), что позволяет
просматривать стек вызовов и локальные переменные. Для установки используется функция swank.core/break Рассмотрим небольшой пример:


(defn add [a b]
   (swank.core/break)
   (+ a b))
Вызовем данную функцию из repl: (add 1 2) =>

BREAK:
[Thrown class java.lang.Exception]

Restarts:
0: [QUIT] Quit to the SLIME top level
1: [CONTINUE] Continue from breakpoint

Backtrace:
0: debug.core$add.invoke(core.clj:4)
1: debug.core$eval1851.invoke(NO_SOURCE_FILE:1)
2: clojure.lang.Compiler.eval(Compiler.java:6465)
3: clojure.lang.Compiler.eval(Compiler.java:6431)
4: clojure.core$eval.invoke(core.clj:2795)
--more--

При наведении курсора на строку 0: debug.core$add.invoke(core.clj:4) и нажатии enter, получим:
BREAK:
[Thrown class java.lang.Exception]

Restarts:
0: [QUIT] Quit to the SLIME top level
1: [CONTINUE] Continue from breakpoint

Backtrace:
0: debug.core$add.invoke(core.clj:4)
Locals:
a = 1
b = 2
1: debug.core$eval1851.invoke(NO_SOURCE_FILE:1)
2: clojure.lang.Compiler.eval(Compiler.java:6465)
3: clojure.lang.Compiler.eval(Compiler.java:6431)
4: clojure.core$eval.invoke(core.clj:2795)
--more--
При нажатии enter на локальной переменной запустится инспектор slime.
При нажатии клавиши 1 - функция продолжит свое выполнение.

Метод 2. Трассировка.

В данном методе отладки используется модуль clojure.contrib.trace.
Важно! При использовании версии clojure 1.3 необходимо указывать ^:dynamic при определении функций для трассировки.
Пример:


(use 'clojure.contrib.trace)

(deftrace sub [a b] (- a b))
(deftrace sub-2 [a b] (- b a))

(defn  func [a b]
  (+ (sub a b) (sub-2 a b))
Результат вызова:
(func 1 2)=>
TRACE t1964: (sub 1 2)
TRACE t1964: => -1
TRACE t1965: (sub-2 1 2)
TRACE t1965: => 1
0

Макрос deftrace выводит название функции, ее аргументы и результат выполнения.
Подробнее про данный модуль можно прочитать тут(trace-api)

Метод 3. Использование отладочного вывода.

В данном случае существует множество методик от простого использование функции print до сложных макросов. Одним из вариантов может служить такой пример:


(defmacro debug-do [& body]
  (when *debug*
    `(do ~@body))
Такой способ позволяет включать отладочный режим установкой переменной *debug* в true.
Пример использования:

(def *debug* 1)

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

(defn add-2 [a b]
  (debug-do (printf "a = %d\n" a)
            (printf "b = %d\n" b)
            (flush))
  (+ 1 2))
При вызове этой функции в repl получаем:
(add-2 1 2)=>
a = 1
b = 2
3