簡體   English   中英

如何從Clojure錯誤中獲得更好的反饋?

[英]How do I get better feedback from Clojure errors?

我發現在我的代碼中調試Clojure錯誤與我使用的所有其他編程語言相比非常困難。 我的主要編程語言是Java,我對Clojure很新。 我寫Clojure的大部分時間都花在試圖弄清楚“為什么我得到這個錯誤?” 我想改變這一點。 我正在使用CounterClockWise作為我的主要IDE。 我不知道如何使用Emacs(但是?)。

這是一個例子:

(ns cljsandbox.core)

(def l [1 2 3 1])

(defn foo
  [l]
  (->> l
    (group-by identity)
    ;vals  ;commented out to show my intent
    (map #(reduce + %))))

在這里,我錯誤地認為group-by返回一個列表列表,但它實際上返回了<key, list<value>>的映射,或者你用Java術語表示它。 這會顯示一條錯誤消息:

ClassCastException clojure.lang.PersistentVector無法強制轉換為java.lang.Number clojure.lang.Numbers.add(Numbers.java:126)

這不是很有用,因為沒有堆棧跟蹤。 如果我輸入(e)它說:

java.lang.ClassCastException: clojure.lang.PersistentVector cannot be cast to java.lang.Number
 at clojure.lang.Numbers.add (Numbers.java:126)
    clojure.core$_PLUS_.invoke (core.clj:944)
    clojure.core.protocols/fn (protocols.clj:69)
    clojure.core.protocols$fn__5979$G__5974__5992.invoke (protocols.clj:13)
    clojure.core$reduce.invoke (core.clj:6175)
    cljsandbox.core$foo$fn__1599.invoke (core.clj:10)
    clojure.core$map$fn__4207.invoke (core.clj:2487)
    clojure.lang.LazySeq.sval (LazySeq.java:42)

我不知道如何從這個錯誤消息中理解,“你以為你將列表列表傳入map但你真的傳遞了一個map數據類型”。 堆棧跟蹤顯示問題是在reduce內部報告的,而不是在group-by內部,但IMO,這不是我作為一個人的地方犯了我的錯誤。 這就是程序發現錯誤的地方。

這些問題可能需要15分鍾才能解決。 我怎樣才能減少時間?


我知道期望動態語言能夠捕獲這些錯誤太過分了。 但是,我覺得像javascript這樣的其他動態語言的錯誤消息更有幫助。

我在這里非常絕望,因為我現在已經在clojure編寫1-2個月了,我覺得我應該更好地解決這些問題。 我嘗試使用:pre / :post on functions但是有一些問題

  1. 報告:pre / :post有點糟糕。 它只打印出你測試的字面。 因此,除非您付出很多努力,否則錯誤消息無濟於事。
  2. 這感覺不是很慣用。 我見過的唯一代碼使用:pre / :post是解釋如何使用的文章:pre / :post
  3. 將線程宏的步驟拉出到自己的defn是非常defn的,這樣我就可以將:pre / :post放在其中。
  4. 如果我虔誠地遵循這種做法,我認為我的代碼可能會變得像Java一樣冗長。 我會手工重塑類型系統。

我已經到了這樣的地方,我用這樣的安全檢查來代碼:

(when (= next-url url)
            (throw (IllegalStateException. (str "The next url and the current url are the same " url))))      
(when-not (every? map? posts-list)
            (throw (IllegalStateException. "parsed-html->posts must return a list of {:post post :source-url source-url}")))

其中只修復了第一個要點。

我也想要

  1. 我有一個非常非常錯誤的開發過程,我不知道
  2. 那里有一些調試工具/庫,我不知道其他人都在做什么
  3. 其他人都有這樣的問題,這是Clojure骯臟的小秘密/其他所有人都習慣使用動態語言,並期望我經歷同樣的努力來解決錯誤
  4. CounterClockWise有一些錯誤,使我的生活變得比它需要的更難
  5. 我應該為我的Clojure代碼編寫比我的Java代碼更多的單元測試。 即使我正在寫一次性代碼。

在這個特定的例子中,發現問題的根源很容易:

  1. 我們有一個函數可以應用於已知的項目向量。 我們也期待一個特定的結果。

  2. 應用該功能會導致問題。 讓我們看看函數內部; 它碰巧是->>管道。

  3. 診斷問題最直接的方法是放棄管道的一些最后階段,看看轉換中的中間階段是否符合我們的預期。

在REPL中做第3步特別簡單; 一種方法是將def輸入和中間結果來臨時瓦爾,另一個是使用*1*2*3 (如果管道很長或者計算需要花費很多時間,我建議每隔幾步至少進行一次臨時def ,否則*n s就足夠了。)

在其他情況下,你會做一些稍微不同的事情,但無論如何將工作分解為可管理的塊以便在REPL中播放是關鍵。 當然,熟悉Clojure的序列和集合庫會加快這個過程的速度; 但是在你正在進行的實際任務的小塊環境中與它們一起玩是了解它們的更好方法之一。

現在理解clojure異常的最好方法(直到可能我們在clojure中使用clojure)是要理解clojure是使用Java類,接口等實現的。所以每當你得到任何這樣的異常時,嘗試映射提到的類/接口在clojure概念的例外。

例如:在您當前的異常中,可以很容易地推斷出clojure.lang.PersistentVector正在嘗試在方法clojure.lang.Numbers.add中輸入clojure.lang.Numbers.addjava.lang.Number 根據這些信息,您可以查看代碼並直觀地找出您在代碼中使用add ie +的位置,然后通過以下事實診斷該問題,即+以某種方式將vector作為參數而不是數字。

我發現clojure.tools.logging / spy宏對於調試非常有用。 它打印出包裝的表達式及其值。 如果你現在不想設置clojure.tools.logging(它需要正常的Java日志記錄配置),你可以使用:

(defmacro spy
  [& body]
  `(let [x# ~@body]
     (printf "=> %s = %s\n" (first '~body) x#)
     x#))

*請記住,如果尚未實現,則上面的代碼不會打印出延遲seq的值。 您可以VEC懶以次來強制實現-以不建議無限seqs。

不幸的是,我沒有找到在線程宏中使用間諜宏的好方法,但它應該足以滿足大多數其他情況。

Dynalint可能值得研究。 它使用額外的檢查來包裝函數調用,這會影響性能,但會提供更好的錯誤消息。

它似乎不是一個非常成熟的項目,並且一年沒有更新,但它已經在改進錯誤消息方面取得了一些進展。 此外,它列在可能的GSoC 2015項目列表中,因此我們很快就會看到很大的改進!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM