2017-06-06 56 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等“标准” –