2017-06-06 51 views
2

大部分情況下,我都明白Clojure告訴我錯誤信息。但我仍然無能爲力,找出錯誤發生的地方。找出Clojure發生錯誤的位置

這裏是我的意思

(defn extract [m] 
    (keys m)) 

(defn multiple [xs] 
    (map #(* 2 %) xs)) 

(defn process [xs] 
    (-> xs 
     (multiple)  ; seq -> seq 
     (extract))) ; map -> seq ... fails 

(process [1 2 3]) 

靜態類型語言,現在會告訴我,我試圖序列傳遞給需要上線X地圖的功能的例子,Clojure中做到這一點在某種程度上:

ClassCastException java.lang.Long cannot be cast to java.util.Map$Entry 

但我仍然不知道錯誤發生在哪裏。很顯然,對於這個例子來說很簡單,因爲只涉及3個函數,您可以輕鬆地通讀所有這些函數,但隨着程序變得越來越大,這個過程變得非常快。

有沒有辦法找出發生錯誤的位置,而不僅僅是從上到下讀取代碼的證據? (這是我目前的做法)

+0

@ChrisMurphy對不起,這是一個錯誤。忘記交換它。我的意思是「多個」。 – TomTom

+0

這裏您的預期產量是多少?鍵應該用於你將矢量傳遞給那個地圖的地圖。像 - >(鍵[2 4 6])不起作用。請檢查這裏的鍵功能:https://clojuredocs.org/clojure.core/keys。 另外(map#(* 2%)xs)不生成哈希映射。也見地圖:https://clojuredocs.org/clojure.core/map –

+0

這是一個錯誤的演示,我知道'keys '確實。我甚至解釋了錯誤發生的原因。我的預期輸出是告訴我發生錯誤的地方。 – TomTom

回答

2

您可以使用clojure.spec。它仍然在alpha版本,並且仍然有一大堆工具支持(希望),但是工具功能運行良好。

(ns foo.core 
    (:require 
    ;; For clojure 1.9.0-alpha16 and higher, it is called spec.alpha 
    [clojure.spec.alpha :as s] 
    [clojure.spec.test.alpha :as stest])) 


;; Extract takes a map and returns a seq 
(s/fdef extract 
    :args (s/cat :m map?) 
    :ret seq?) 

(defn extract [m] 
    (keys m)) 


;; multiple takes a coll of numbers and returns a coll of numbers 
(s/fdef multiple 
    :args (s/cat :xs (s/coll-of number?)) 
    :ret (s/coll-of number?)) 

(defn multiple [xs] 
    (map #(* 2 %) xs)) 


(defn process [xs] 
    (-> xs 
     (multiple)  ; seq -> seq 
     (extract))) ; map -> seq ... fails 

;; This needs to come after the definition of the specs, 
;; but before the call to process. 
;; This is something I imagine can be handled automatically 
;; by tooling at some point. 
(stest/instrument) 

;; The println is to force evaluation. 
;; If not it wouldn't run because it's lazy and 
;; not used for anything. 
(println (process [1 2 3])) 

運行此文件打印(以及其他信息):

Call to #'foo.core/extract did not conform to spec: In: [0] val: (2 
4 6) fails at: [:args :m] predicate: map? :clojure.spec.alpha/spec 
#object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x2b935f0d 
"[email protected]"] 
:clojure.spec.alpha/value ((2 4 6)) :clojure.spec.alpha/args ((2 4 
6)) :clojure.spec.alpha/failure :instrument 
:clojure.spec.test.alpha/caller {:file "core.clj", :line 29, 
:var-scope foo.core/process} 

這可以解讀爲:以exctract調用失敗,因爲在(2 4 6)傳遞的值未能斷言map?。該呼叫發生在第29行的文件"core.clj"中。

讓人們注意的一個注意事項是儀器只檢查函數參數而不返回值。 Rich Hickey設計決定是一個(奇怪的是,如果你問我)。有a library for that, though

0

如果你有一個REPL會話可以打印一個堆棧跟蹤:

(clojure.stacktrace/print-stack-trace *e 30) 

打印的堆棧跟蹤各種不同的方式請參見http://puredanger.github.io/tech.puredanger.com/2010/02/17/clojure-stack-trace-repl/。你將需要有一個依賴像這樣在你的project.clj

[org.clojure/tools.namespace "0.2.11"] 

我沒有用以上方法無法獲得堆棧跟蹤,但是隻是在打字REPL *e會給大家介紹了可用信息錯誤,說實話看起來不是很有幫助。

對於極少數情況下,堆棧跟蹤沒有幫助,我通常使用調用來調用函數,該函數返回給定的單個參數,但具有打印該參數的副作用。我碰巧稱這個功能爲probe。在你的情況下,它可以放在線程宏的多個位置。

+0

'* e'告訴我在基礎庫中發生錯誤的位置。但我想看看我的代碼中發生錯誤的位置。 – TomTom

0

重新輸入您的例子,我有:

(defn extract [m] 
    (keys m)) 

(defn multiply [xs] 
    (mapv #(* 2 %) xs)) 

(defn process [xs] 
    (-> xs 
    (multiply)  ; seq -> seq 
    (extract)))  ; map -> seq ... fails ***line 21*** 

(println (process [1 2 3])) 
;=> java.lang.ClassCastException: java.lang.Long cannot be cast 
to java.util.Map$Entry, compiling:(tst/clj/core.clj:21:21) 

所以我們得到了一個很好的線索在是例外說,文件和行/列數tst.clj.core.clj:21:21extract方法的問題。

我使用的另一個不可或缺的工具是Plumatic Sc​​hema,將「漸進」類型檢查注入clojure。代碼變爲:

(ns tst.clj.core 
    (:use clj.core tupelo.test) 
    (:require 
    [tupelo.core :as t] 
    [tupelo.schema :as tsk] 
    [schema.core :as s])) 
(t/refer-tupelo) 
(t/print-versions) 

(s/defn extract :- [s/Any] 
    [m :- tsk/Map] 
    (keys m)) 

(s/defn multiply :- [s/Num] 
    [xs :- [s/Num]] 
    (mapv #(* 2 %) xs)) 

(s/defn process :- s/Any 
    [xs :- [s/Num]] 
    (-> xs 
    (multiply) ; seq -> seq 
    (extract))) ; map -> seq ... fails 

(println (process [1 2 3])) 

clojure.lang.ExceptionInfo: Input to extract does not match schema: 
[(named (not (map? [2 4 6])) m)] {:type :schema.core/error, :schema [#schema.core.One{:schema {Any Any}, 
:optional? false, :name m}], 
:value [[2 4 6]], :error [(named (not (map? [2 4 6])) m)]}, 
compiling:(tst/clj/core.clj:23:17) 

所以,雖然該錯誤消息的格式是一個有點冗長,它告訴馬上我們傳遞了錯誤類型的參數和/或捏成方法extract

請注意,你需要這樣一行:

(s/set-fn-validation! true) ; enforce fn schemas 

我創建了一個特殊的文件test/tst/clj/_bootstrap.clj所以它總是在同一個地方。

有關Plumatic架構的詳細信息,請參閱:

+0

我沒有得到一個行號只是例外。你如何設置東西來獲得行號?由於我的帖子,我已經看過規範。在某些時候關注Clojure的標準可能更爲明智? – TomTom

+0

Re line#:我不使用repl太多,所以我只是把所有內容放在一個文件中並使用'lein test'。 Re規格:我認爲Schema更易於使用,而且非常普遍。它幾乎是一個像Ring等「標準」 –