2024年Clojure调查问卷中分享您的想法!

欢迎!有关如何使用本站的信息,请参阅关于页面。

0
Spec
这个问题是在这次讨论的上下文中发现的 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] 中失败,断言:(: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的错误报告补丁。

观察到 especs 对 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 失败规范::clojure.core.specs/ns-refer-clojure 在:[:args :clauses :refer-clojure :clause],断言:#{:refer-clojure}
输入:[1 0] val:require 失败规范::clojure.core.specs/ns-require 在:[:args :clauses :require :clause],断言:#{:require}
输入:[1 0] val:require 失败规范::clojure.core.specs/ns-import 在:[:args :clauses :import :clause],断言:#{:import}
输入:[1 0] val:require 失败规范::clojure.core.specs/ns-use 在:[:args :clauses :use :clause],断言:#{:use}
输入:[1 0] val:require 失败规范::clojure.core.specs/ns-refer 在:[:args :clauses :refer :clause],断言:#{:refer}
输入:[1 0] val:require 失败规范::clojure.core.specs/ns-load 在:[:args :clauses :load :clause],断言:#{:load}
输入:[1 0] val: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 能按 :path 的长度对 ::s/problems 排序会更理想,这将会把前两个非预期选项推到最后。


(s/explain (s/cat :maybe-num (s/? number?)
           

     ["3"])


现在给出


输入:[0] val:"3" 失败在:[:maybe-num],断言:number?
在:[0] val:`"3"` 在:[:keyword] 中失败,断言:keyword?


虽然可以编造例子,让这种报告产生更多噪音(如 defn),但我相信这在上文所述原因和

- 当出错时,我们程序员总是要求用户提供最具体的信息 - 将这种做法应用于规范错误报告也是正确的
- 自定义错误报告器 (s/*explain-out*) 获取更多数据来生成更符合用户意图的狭窄报告

7 答案

0
by
评论者:visibletrap

我会在这里提出一个稍有不同的问题,因为我认为它有相同的根本原因。

它应该特别指出 fspec 的 :ret 缺失,但实际上它在 :args 处失败。


(require '[clojure.spec :as s])
(require '[clojure.spec.test :as st])

(defn x [f] (f 1))

(s/fdef x
  :args (s/cat :f (s/fspec :args (s/cat :i int?))))

(st/instrument `x)

(x (fn [a] a))



主线程 "main" 中抛出异常 clojure.lang.ExceptionInfo:对 #"user/x" 的调用不符合规范
输入:[0] val:(user$eval20$fn__21 0x3e521715 "user$eval20$fn__21@3e521715") 失败在:[:args],断言:(cat :f (fspec :args (cat :i int))), 额外输入
:clojure.spec/args (user$eval20$fn__21 0x3e521715 "user$eval20$fn__21@3e521715")
:clojure.spec/failure :instrument
:clojure.spec.test/caller {:file "debug.clj", :line 16, :var-scope user/eval20}
{:clojure.spec/problems [{:path [:args], :reason "Extra input", :pred (cat :f (fspec :args (cat :i int))), :val (user$eval20$fn__21 0x3e521715 "user$eval20$fn__21@3e521715"), :via [], :in [0]}], :clojure.spec/args (user$eval20$fn__21 0x3e521715 "user$eval20$fn__21@3e521715"), :clojure.spec/failure :instrument,
...
0
by

评论由:lgs32a 提出

@alexmiller:在发布1.9版本之前决定这个怎么样?因为它在很大程度上改进了错误信息。自从在小组中原始讨论之后,有更多关于不直观错误信息的报告,这将得到解决。与此相关的是根据路径长度对解释进行排序,如上所述(这还没有票据)。
请让Rich提供一些意见。

0

评论由:gshayban 提出

目前与规范并行进行,并且在一个不受Clojure 1.9发布影响的单独的Git仓库中发生。我们将能够独立于Clojure更新规范依赖关系,并在最终得到修复。

0

评论由:lgs32a 提出

这影响了Clojure的编译时错误信息,而不仅仅是spec的opt-in部分。这个编译器错误报告的部分是损坏的,不应该与主发布一起发货。在这方面,spec是否是一个独立的依赖项,实际上并不重要。

另外,等待spec稳定版本的用户不一定会更新他们的spec依赖关系。

0

评论由:lgs32a 提出

AFAIK,更好的错误报告是Spec成为1.9依赖的唯一原因 - 请重新考虑。

0

评论由:alexmiller 提出

我不知道这将在1.9之前还是之后被考虑。根据我所阅读的内容,这似乎是合理的。

根据路径长度排序的确有一个在CLJ-2063的票据,该票据在5月被应用,并在spec.alpha 0.1.109中发布。

0
参考:https://clojure.atlassian.net/browse/CLJ-2013(由lgs32a报告)
...