请分享您的看法,参与 Clojure 2024年现状调查!

欢迎!请查看关于页面,了解更多有关如何使用此网站的信息。

0
Spec
此问题是在以下讨论的上下文中发现的:https://groups.google.com/d/msg/clojure/mIlKaOiujlo/tF71zZ2BCwAJ

一个简化的如何导致规格错误报告失败的示例如下

他使用了一个无效的命名空间形式

(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那样的错误报告补丁。

已观察到规格对具有可选分支的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] 值: 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)


如果是解释程序按照 ::s/problems 中 :path 的长度排序会更好,这将把前两个未预期选项推到最后。


(s/explain (s/cat :maybe-num (s/? number?)
    
    ["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] 断言: (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
by

评论由: lgs32a 制作

@alexmiller: 在发布1.9之前决定这件事情吧,因为它将极大地改进错误信息。自从在群里进行原始讨论之后,又有更多的报告称不直观的错误信息得到了修复。与此相关的是根据路径长度排序解释,如上图所示(该问题上还没有创建ticket)。
请让Rich提出一些意见。

0

评论:gshayban

正在进行spec的规格工作,并在不受Clojure 1.9发布影响的单独git仓库中进行。我们将能够独立于Clojure更新spec依赖关系,并最终收到修复。

0

评论由: lgs32a 制作

这会影响Clojure的编译时错误信息,而不仅仅是spec的opt-in部分。这部分编译错误报告是错误的,不应用于主要版本发布。在这方面,spec是单独的依赖关系还是不是真的无关紧要。

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

0

评论由: lgs32a 制作

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

0

评论:alexmiller

我不知道这是在1.9之前还是之后考虑。从我所读到的内容来看,这似乎是合理的。

按照路径长度排序的确有CLJ-2063这个ticket,它在5月被应用,并于spec.alpha 0.1.109发布。

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