此问题在该讨论的上下文中被发现
https://groups.google.com/d/msg/clojure/mIlKaOiujlo/tF71zZ2BCwAJ
关于 specs 错误报告失败的示例,看起来是这样的
他使用了无效的 ns 形式
(ns foo (require [clojure.spec :as s])) ; 应该是 :require
spec 报告的错误
在:[1] val: ((require [clojure.spec :as s])) 在:[:args] 断言:(:docstring (? string?) :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses), 额外输入
:clojure.spec/args (foo (require [clojure.spec :as s]))
clojure.core/ex-info (core.clj:4725)
尽管错误在技术上是真实的,但它并没有显示用户报告的每个 s/cat 的替代选项是如何失败的。
为了更好地了解用户数据为什么会错误,他应该确切知道 spec 所尝试的以及如何失败。
一个如何工作的良好例子是 s/alt,其中所有失败的替代选项都会始终报告给用户。
首先通过实验,然后在 specs 代码中进行调查,这个问题已经被研究。 最后,一个上传的补丁实现了类似于 s/alts 的错误报告。
对于具有可选分支的 cat 的 specs 错误报告行为的观察如下
1. 如果 cat 在一个或多个可选分支之后失败,整个 cat 被报告为失败
2. 如果 cat 在一个或多个可选分支之后/并/后续有必填分支失败,只有后续的必填分支被报告,对替代的可选分支没有说明。
规则 1 解释了 ns 的例子。
规则 2 的失败可能会导致用户的直觉有更坏的情况
(s/explain (s/cat :maybe-num (s/? number?'
:keyword keyword?)
["3"])
给出
在:[0] val: "3" 在:[:keyword] 断言:keyword?
报告清楚地没有提及用户想要输入数字。 相反,他会被误导,认为他应该输入关键字。
解决方案
已经编写了一个简单的补丁,它将 op-explain 修改为以下行为
- 均报告 s/cat 中已尝试的所有替代选项。
这显著改进了报告的错误,因为它清晰地显示了用户数据如何未通过验证。
(ns foo (require [clojure.spec :as s])) ; 应该是 :require
现在给出
ExceptionInfo 对 clojure.core/ns 的调用不符合规范
在:[1] val: (require [clojure.spec :as s]) 在:[:args :docstring] 断言:string?
在:[1] val: (require [clojure.spec :as s]) 在:[:args :attr-map] 断言:map?
在:[1 0] val: require 不符合规范::clojure.core.specs/ns-refer-clojure 在:[:args :clauses :refer-clojure :clause] 断言:#{:refer-clojure}
输入:[1 0] 值:require 失败,规范::clojure.core.specs/ns-require 在位置:[:args :clauses :require :clause],谓词:#{:require}
输入:[1 0] 值:require 失败,规范::clojure.core.specs/ns-import 在位置:[:args :clauses :import :clause],谓词:#{:import}
输入:[1 0] 值:require 失败,规范::clojure.core.specs/ns-use 在位置:[:args :clauses :use :clause],谓词:#{:use}
输入:[1 0] 值:require 失败,规范::clojure.core.specs/ns-refer 在位置:[:args :clauses :refer :clause],谓词:#{:refer}
输入:[1 0] 值:require 失败,规范::clojure.core.specs/ns-load 在位置:[:args :clauses :load :clause],谓词:#{:load}
输入:[1 0] 值:require 失败,规范::clojure.core.specs/ns-gen-class 在位置:[:args :clauses :gen-class :clause],谓词:#{:gen-class}
:clojure.spec/args (foo (require [clojure.spec :as s]))
clojure.core/ex-info (core.clj:4725)
如果 explain-data 能够根据 ::s/problems 的 :path 长度进行排序,这将把前两个非预期选项移至末尾,会更好。
(s/explain (s/cat :maybe-num (s/? number?'
:keyword keyword?)
["3"])
现在给出
输入:[0] 值:"3" 失败,在位置:[:maybe-num],谓词:number?
在:[0] val: "3" 在:[:keyword] 断言:keyword?
虽然可以构建一些示例,其中这种报告会产生更多的噪音(如 defn),但我相信由于上述原因,这是正确的权衡。
- 当出现问题时,我们程序员总是要求用户提供最具体的信息 - 将其应用到规范错误报告中是正确的
- 自定义错误报告器(s/*explain-out*)可以获得更多信息,从而更好地生成符合用户意图的所需报告