*说明*
如果你对一个函数进行 instrument,你可能会得到如下类似的 spec 错误
(defn f [x] (inc x))
(s/fdef f
:args (s/cat :x (s/and integer? even?))
:ret (s/and integer? odd?))
(t/instrument)
(f 3)
;; ExceptionInfo 叫做 'user/f' 的调用没有符合 spec
;; In: [0] val: 3 在: [:args :x]谓词: even?
;; :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__1200
0x19b3f9a "clojure.spec.alpha$regex_spec_impl$reify__1200@19b3f9a"]
;; :clojure.spec.alpha/value (3)
;; :clojure.spec.alpha/args (3)
;; :clojure.spec.alpha/failure :instrument
;; :clojure.spec.test.alpha/caller {:file "form-init3240393046310519022.clj", :lin
e 1, :var-scope user/eval1413}
;; clojure.core/ex-info (core.clj:4725)
(ex-data *e)
;; {:clojure.spec.alpha/problems
;; [{:path [:args :x],
;; :pred clojure.core/even?,
;; :val 3,
;; :via [],
;; :in [0]}],
;; :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x19b3f9a "clojure.spec.alpha$regex_spec_impl$reify__1200@19b3f9a"],
;; :clojure.spec.alpha/value (3),
;; :clojure.spec.alpha/args (3),
;; :clojure.spec.alpha/failure :instrument,
;; :clojure.spec.test.alpha/caller {:file "form-init3240393046310519022.clj", :line 1, :var-scope user/eval1413}}
正如你所见,
- explain-data 中包含一个正则表达式(即 f 的参数 spec),为 {{::s/spec}}
- 每个问题在其 {{:path}} 中包含 {{:args}}
这些事实可能会让 spec 错误报告工具产生困惑,因为 f 的参数 spec({{(s/and integer? even?)}})并没有与 {{:args}} 关键字对应的子 spec(我认为 {{:path}} 应该只包含指示从 spec 中选择哪个子 spec 的线索的关键字)。
*可能的解决方案*
为了解决这种令人困惑的情况,并改进 instrument 检查的 explain-data 的一致性,我认为有两种选择如下
- *解决方案 1.* 从 {{:path}} 中删除 {{:args}}
- *解决方案 2.* 修改 instrument 检查的 explain-data,使其具有 fspec(而不是它的 {{:args}})作为 {{::s/spec}}
我个人更倾向于 *解决方案 2.* 因为在 explain-data 中添加 fspec 可以使提供丰富错误信息给 {{*explain-out*}} 实现者成为可能。
同样适用于 {{macroexpand-check}}。