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} fails spec: :user/e predicate: (contains? % :user/c)
val: #:user{:a 1, :b 0} fails spec: :user/e predicate: (< (:user/a %) (:user/b %))


s/merge具有“satisfies all”的语义,但当然只针对映射。
0 投票

评论者:dchelimsky

明白了。我的特定问题的另一部分,在这示例中没有反映出来,就是我有一个需要在最后谓词中引用的中间构形器。s/merge在该情况下将不起作用,因为构形器无法生成。您想要为这个问题创建一个单独的问题吗?

0 投票

评论者:dchelimsky

所以,我几乎用自定义生成器完成了这个。你希望留下这个问题吗?

0 投票

评论者:alexmiller

关于构形器 - 不是的,构形值不流入s/merge是按设计(且与“满足全部”相关的“不流入”概念一致)。关于留下它开放,由你决定。我们讨论过创建一个不流入和可变控形器,但我想没有为它创建工单。

0 投票
_评论者:dchelimsky_

实际上,我认为我刚刚发现了一个与此相关的bug。


user=> (s/explain ::e {::a 1 ::b 2})
val: #:user{:a 1, :b 2} 不满足 spec: :user/e 谓词: (contains? % :user/c)
val: #:user{:a 1, :b 2} 不满足 spec: :user/e 谓词: (< (:user/a %) (:user/b %))
nil


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

评论者:dchelimsky

我将为它创建一个单独的问题。

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