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

欢迎!请参阅关于页面以获取更多关于如何使用本网站的信息。

0
Spec
考虑以下示例


$ clj
Clojure 1.9.0
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def ::a int?)
:user/a
user=> (s/def ::b int?)
:user/b
user=> (s/def ::c int?)
:user/c
user=> (s/def ::d (s/and (s/keys :req [::a ::b ::c]) #(< (::a %) (::b %))))
:user/d
user=> (s/explain-data ::d {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/contains? % :user/c)), :val #:user{:a 1, :b 0}, :via [:user/d], :in []}), :spec :user/d, :value #:user{:a 1, :b 0}}
user=> (s/explain-data ::d {::a 1 ::b 0 ::c 2})
#:clojure.spec.alpha{:problems [{:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 1, :b 0, :c 2}, :via [:user/d], :in []}], :spec :user/d, :value #:user{:a 1, :b 0, :c 2}}


我希望 explain-data 的第一次调用能够包含缺少的 ::c 以及失败的小于(<)谓词。我理解 spec/and 是设计为短路,因此或许可以通过一个新的处理 n 个 spec 的函数来解决此问题。

7 个回答

0
_评论者:alexmiller_

在这种情况下,您可以使用 s/merge 来获取所有问题...


user=> (s/def ::e (s/merge (s/keys :req [::a ::b ::c]) #(< (::a %) (::b %))))
:user/e
user=> (s/explain ::e {::a 1 ::b 0})
val: #:user{:a 1, :b 0} 无法满足 spec: :user/e 谓词: (contains? % :user/c)
val: #:user{:a 1, :b 0} 无法满足 spec: :user/e 谓词: (< (:user/a %) (:user/b %))


s/merge 有 "满足所有" 的语义,但当然是特定于映射的。
0

评论人:dchelimsky

明白了。我遇到的具体问题的另一部分没有在这个例子中体现,那就是中间有一个形状分子是我最后一个子句依赖的。s/merge在这种情况下无法工作,因为形状分子无法生成。您想要为这件事单独创建一个问题吗?

0

评论人:dchelimsky

因此,我使用自定义生成器让这个功能接近完成。你希望留下这个问题吗?

0

评论人:alexmiller

关于形状分子,不是的,设计时就决定在s/merge中不传递已验证的值,这与“满足所有条件”的“不传递”概念是一致的。至于是否开放,这取决于你。我们曾讨论过创建一个不传递的变体,但我不知道是否有相应的工单。

0
_评论人:dchelimsky_

实际上,我刚刚发现一个与此相关的错误。根据上述示例


user=> (s/explain ::e {::a 1 ::b 2})
val: #:user{:a 1, :b 2} fail spec: :user/e predicate: (contains? % :user/c)
val: #:user{:a 1, :b 2} fails spec: :user/e predicate: (< (:user/a %) (:user/b %))
nil


这应该通过 {{(< (:user/a %) (:user/b %))}}
0

评论人:dchelimsky

我将为这件事单独创建一个问题。

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