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}}


我想有一种方法可以获得解释数据的第一调用,包括缺少的 ::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 有 "satisfies all" 的语义,但当然仅适用于映射。
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} spec评估失败::user/e谓词:包含?%(: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报告)
...