2024年Clojure状态调查中分享您的看法!

欢迎!请参阅关于页面以了解有关如何操作的一些更多信息。

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]谓词: (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 对具有可选分支的 cat 的错误报告行为如下

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不符合spec
在: [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] 值: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能按:path的长度对 ::s/problems进行排序,将第一个两个意外选项移到末尾会更好。


(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*)获取更多数据,生成更符合用户意图的狭窄报告

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))



主线程中抛出异常 clojure.lang.ExceptionInfo:寄存器调用:#'-user/x'未能符合规范
输入:[0] 值:(#object[user$eval20$fn__21 0x3e521715 "user$eval20$fn__21@3e521715"])失败位置:[:args] 判断条件:(:f (fspec :args (: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 (:f (fspec :args (: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
by

评论由:lgs32a发表

@alexmiller: 在发布1.9之前决定这件事怎么样?因为它在很大程度上了改进了错误信息。自从在组中的最初讨论之后,有更多的关于不直观的错误信息的报告,这将会解决这个问题。与之相关的是按路径长度排序解释,如上所述(目前还没有创建票据)。
请征询Rich的意见。

0

评论是由 gshayban 发表的。

规范开发正在进行中,并且在一个独立的 git 仓库中工作,这个仓库不会受到 Clojure 1.9 释放的影响。我们将能够独立于 Clojure 更新规范依赖项,并最终接收修复。

0

评论由:lgs32a发表

这影响了 Clojure 的编译时间错误消息,而不仅仅是 spec 的选择部分。这部分编译器错误报告有问题,不应该与重大版本一起发布。在这方面,spec 是单独的依赖项还是不是并不重要。

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

0

评论由:lgs32a发表

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

0

评论是由 alexmiller 发表的。

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

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

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