参与关于 Clojure 2024 状态的调查!请点击2024 State of Clojure Survey!

欢迎!请访问关于页面以了解如何使用的更多信息。

0
Spec
该问题在以下讨论中检测到: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] 上失败 predicate: (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 的每个替代选项是如何失败。

为了更好地理解为什么用户数据不正确,他应该确切知道 spec 尝试了什么以及它是如何失败的。

这是一个很好说明如何工作的例子,即 s/alt,其中所有失败的替代项总是报告给用户。

这个问题已经被调查过,首先是实验性的,然后在 specs 代码中。  最后,附上一个修复程序,类似于 s/alts 的错误报告。

观察到的 specs 错误报告行为如下

1. 如果 cat 在一个或多个可选分支后失败,则报告整个 cat 失败
2. 如果 cat 在一个或多个可选分支后 /并且/一个随后的必需分支失败,则只报告随后的必需分支,不对替代的可选分支做任何备注。

规则 1 解释了 ns 的示例。
规则 2 可能会严重影响用户的直觉


(s/explain (s/cat :maybe-num (s/? number?)
                  :keyword keyword?)
           ["3"])


给出


在:[0] val: "3" 在:[:keyword] 上失败 predicate: keyword?


报告明确没有解决用户想要输入数字的问题。  相反,他可能会认为他应该输入一个关键字。

解决方案

已编写一个简单的补丁,改变 op-explain 以具备以下行为

- s/cat 中尝试的所有替代选项都要单独报告。

它显著提高了报告的错误,因为它清楚地说明了用户数据是如何未通过验证的。


(ns foo (require [clojure.spec :as s])) ; 应为 :require
 

现在给出


ExceptionInfo 调用 clojure.core/ns 未符合 spec
在:[1] val: (require [clojure.spec :as s]) 在:[:args :docstring] 上失败 predicate: string?
在:[1] val: (require [clojure.spec :as s]) 在:[:args :attr-map] 上失败 predicate: map?
在:[1 0] val: require 未通过 spec::clojure.core.specs/ns-refer-clojure 在:[:args :clauses :refer-clojure :clause] 上失败 predicate: #{: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?)
                  :keyword keyword?)
           ["3"])


现在给出


输入:[0] val:"3"失败位置:[:maybe-num]谓词:number?
在:[0] val: "3" 在:[:keyword] 上失败 predicate: keyword?


虽然可以编造出这种报告会产生更多噪音的例子(如defn),但我相信考虑到上述原因,这是一个正确的权衡。

—— 我们程序员在出错时总是要求用户提供尽可能详细的信息 - 将同样的应用适用于规格错误报告是正确的
—— 自定义错误报告器(s/*explain-out*)可以获得更多数据,以便更好地生成符合用户意图的狭窄报告

7 个答案

0
_评论者: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))



在主线程中抛出异常 clojure.lang.ExceptionInfo: 对#'user/x的调用未符合规格
输入:[0] val:(object[user$eval20$fn__21 0x3e521715 "user$eval20$fn__21@3e521715"])失败位置:[:args]谓词:(cat :f (fspec :args (cat :i int))),  额外输入
:clojure.spec/args  (#object[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 (#object[user$eval20$fn__21 0x3e521715 "user$eval20$fn__21@3e521715"]), :via [], :in [0]}], :clojure.spec/args (#object[user$eval20$fn__21 0x3e521715 "user$eval20$fn__21@3e521715"]), :clojure.spec/failure :instrument,
...
0

评论者:lgs32a

@alexmiller: 在发布1.9之前决定这个怎么样,因为它以重大方式改进了错误消息。自从在小组中最初的讨论之后,有更多关于不直观错误消息的报告,这将解决这些问题。与这相关的是按路径长度对解释进行排序,如上所示(目前还没有对此的票证)。
请从Rich那里获取一些输入。

0

评论者:gshayban

正在根据规范开展工作,并在单独的git repo中进行,不会受Clojure 1.9版本发布的影响。我们可以在不依赖于Clojure的情况下独立更新规范依赖,并接收最终的修复。

0

评论者:lgs32a

这会影响Clojure的编译时间错误消息,而不仅仅是spec的opt-in部分。编译器错误报告的这一部分是破坏性的,不应该与主要版本一起发布。在这方面,spec是一个独立依赖或不独立依赖实际上并不重要。

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

0

评论者:lgs32a

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

0

评论者:alexmiller

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

根据路径长度排序的功能在CLJ-2063上有票,该票在5月份应用并在spec.alpha 0.1.109中发布。

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