此问题是在以下讨论的背景下检测到的
https://groups.google.com/d/msg/clojure/mIlKaOiujlo/tF71zZ2BCwAJ
以下是一个规格错误报告失败的示例,它看起来像这样
他使用了一个无效的ns形式
(ns foo (require [clojure.spec :as s])) ; 应该是 :require
规格报告的错误
在:[1] val:((require [clojure.spec :as s])) 在 [:args] 处失败:谓词:(cat :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的替代选项失败。
为了更好地理解为什么用户数据不正确,他应该确切知道规格尝试了什么以及它如何失败。
好的,s/alt是一个很好的例子,其中所有失败的替代选项都始终报告给用户。
已调查此问题,首先在现场实验中,然后在规格代码中。 最后,附带了类似s/alts的错误报告补丁。
已观察到spec为具有可选分支的cat的错误报告行为如下:
1. 如果cat在经过一个或多个可选分支后失败,则整个cat报告为失败。
2. 如果cat在经过一个或多个可选分支/和/随后的必需分支后失败,则只报告随后的必需分支,而对替代的任意分支不进行说明。
规则1解释了ns示例。
规则2可能严重违反用户的直观感觉
(s/explain (s/cat :maybe-num (s/? number?)
["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 失败于 spec::clojure.core.specs/ns-refer-clojure,失败在:[:args :clauses :refer-clojure :clause],谓词:#{:refer-clojure}
输入:[1 0] val:require 失败于 spec::clojure.core.specs/ns-require,失败在:[:args :clauses :require :clause],谓词:#{:require}
输入:[1 0] val:require 失败于 spec::clojure.core.specs/ns-import,失败在:[:args :clauses :import :clause],谓词:#{:import}
输入:[1 0] val:require 失败于 spec::clojure.core.specs/ns-use,失败在:[:args :clauses :use :clause],谓词:#{:use}
输入:[1 0] val:require 失败于 spec::clojure.core.specs/ns-refer,失败在:[:args :clauses :refer :clause],谓词:#{:refer}
输入:[1 0] val:require 失败于 spec::clojure.core.specs/ns-load,失败在:[:args :clauses :load :clause],谓词:#{:load}
输入:[1 0] val:require 失败于 spec::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 能够根据它们的 :path 长度对 ::s/problems 进行排序,那就更好了,这将把前两个意外选项推到最后。
(s/explain (s/cat :maybe-num (s/? number?)
["3"])
(较小地纠正版)
现在给出了
输入:[0] val:"3" 失败于:[:maybe-num],谓词:number?
在:[0] val:"3" 在 [:keyword] 处失败:谓词:keyword?
虽然可以找到这类报告产生更多噪音的例子(如 defn),但根据上述原因,我认为这是正确的权衡。
- 我们程序员总是在出错时向用户索取最具体的信息 - 将同样的方法应用于 spec 错误报告是正确的。
- 在线错误报告器(s/*explain-out*)可以获得更多数据来生成更匹配用户意图的狭窄报告。