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

欢迎!请查看 关于 页面以了解如何工作的更多信息。

0
规范
此问题是在以下讨论中发现的 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 的错误报告的补丁。

观察到规范对于具有可选分支的 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 的调用不符合规范
在:[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] 值: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 可以根据 ::s/problems 的 :path 长度进行排序,那就更好了,这样可以将前两个不合理的选项移到末尾。


(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
_评论者: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] 值:(#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 仓库中完成,不受 Clojure 1.9 版本发布的影响。我们将能够独立于 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 报告)
...