当使用repl时,调试Clojure代码的最佳方法是什么?


当前回答

使用repl调试Clojure代码的最佳方法

稍微偏左,但是“使用REPL本身”。

我写Clojure已经有一年多的时间了,并没有觉得对任何调试工具有很大的需求。如果你保持你的函数很小,并且在REPL中运行每个函数,并观察结果,那么你应该可以非常清楚地了解你的代码是如何行为的。

我发现调试器对于观察正在运行的应用程序中的STATE最有用。Clojure使使用不可变数据结构(不变状态)的函数式风格编写变得容易(而且有趣!)这大大减少了对调试器的需求。一旦我知道所有组件的行为都符合我的预期(特别注意事物的类型),那么大规模的行为就很少会成为问题。

其他回答

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

从2016年开始,你可以使用Debux,这是Clojure/Script的一个简单调试库,可以与repl以及浏览器的控制台一起工作。您可以在代码中添加dbg (debug)或clog (console.log)宏,并轻松观察各个函数的结果,等等,打印到您的REPL和/或控制台。

来自项目的Readme:

Basic usage This is a simple example. The macro dbg prints an original form and pretty-prints the evaluated value on the REPL window. Then it returns the value without interfering with the code execution. If you wrap the code with dbg like this, (* 2 (dbg (+ 10 20))) ; => 60 the following will be printed in the REPL window. REPL output: dbg: (+ 10 20) => 30 Nested dbg The dbg macro can be nested. (dbg (* 2 (dbg (+ 10 20)))) ; => 60 REPL output: `dbg: (+ 10 20) => 30` dbg: (* 2 (dbg (+ 10 20))) => 60

还有dotrace,它允许您查看所选函数的输入和输出。

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

您需要将^:dynamic添加到函数定义中

(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

使用spyscope实现一个自定义阅读器宏,这样你的调试代码也是生产代码 https://github.com/dgrnbrg/spyscope

下面是一个调试复杂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)))

...以及一篇解释其用途的文章。