[英]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但是有一些問題
:pre
/ :post
有點糟糕。 它只打印出你測試的字面。 因此,除非您付出很多努力,否則錯誤消息無濟於事。 :pre
/ :post
是解釋如何使用的文章:pre
/ :post
。 defn
是非常defn
的,這樣我就可以將:pre
/ :post
放在其中。 我已經到了這樣的地方,我用這樣的安全檢查來代碼:
(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}")))
其中只修復了第一個要點。
我也想要
在這個特定的例子中,發現問題的根源很容易:
我們有一個函數可以應用於已知的項目向量。 我們也期待一個特定的結果。
應用該功能會導致問題。 讓我們看看函數內部; 它碰巧是->>
管道。
診斷問題最直接的方法是放棄管道的一些最后階段,看看轉換中的中間階段是否符合我們的預期。
在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.add
為java.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。
不幸的是,我沒有找到在線程宏中使用間諜宏的好方法,但它應該足以滿足大多數其他情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.