请在 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 个规范来解决这个问题。

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} 无法满足规范::user/e 断言:contains? (% :user/c)
val: #:user{:a 1, :b 0} 无法满足规范::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} fails 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报告)
...