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

欢迎!请参见 关于 页面以了解有关如何使用本网站的一些更多信息。

0
Spec
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 (s/merge (s/keys :req [::a ::b]) #(< (::a %) (::b %))))
:user/c
user=> (s/explain-data ::c {::a 0 ::b 1})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 0, :b 1}, :via [:user/c], :in []}), :spec :user/c, :value #:user{:a 0, :b 1}}
user=> ;; ^^ 不符合预期
user=> (s/explain-data ::c {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 1, :b 0}, :via [:user/c], :in []}), :spec :user/c, :value #:user{:a 1, :b 0}}
user=> ;; ^^ 符合预期
user=> (s/def ::a<b #(< (::a %) (::b %)))
:user/a<b
user=> (s/def ::d (s/merge (s/keys :req [::a ::b]) ::a<b))
:user/d
user=> (s/explain-data ::d {::a 0 ::b 1})
nil
user=> (s/explain-data ::d {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 1, :b 0}, :via [:user/d :user/a<b], :in []}), :spec :user/d, :value #:user{:a 1, :b 0}}

10 答案

0
_评论由: dchelimsky_ 提出:

当引用谓词函数时,它也会总是失败,因此要使其正常工作,唯一的方法是定义另一个规范并引用该规范。


user=> (defn a<b [a b] (< a b))
#'user/a<b
user=> (s/def ::e (s/merge (s/keys :req [::a ::b]) a<b))
:user/e
user=> (s/explain-data ::e {::a 1 ::b 2})
#:clojure.spec.alpha{:problems ({:path [], :pred user/a<b, :val #:user{:a 1, :b 2}, :via [:user/e], :in []}), :spec :user/e, :value #:user{:a 1, :b 2}}
user=> (s/explain-data ::e {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred user/a<b, :val #:user{:a 1, :b 0}, :via [:user/e], :in []}), :spec :user/e, :value #:user{:a 1, :b 0}}
0

由 alexmiller 评论

(<::a %% ::b %%)) 看起来不太对? #(< (::a %%) (::b %%)) 能工作吗?

0

由 dchelimsky 评论

好一个插曲,然而没有 #(< (::a %%) (::b %%)) 在行内工作是不可行的。我已经在例子中做了相应的更新。

0

由 dchelimsky 评论

另外,FWIW,我在我的打字错误之前就缩小了问题所在。碰巧的是,无论是带还是不带这个错误,我都得到了同样的答案。

0
_评论由: dchelimsky_ 提出:

如果你用 {s/spec} 包围谓词,它是可以工作的。


user=> (s/def ::f (s/merge (s/keys :req [::a ::b]) (s/spec #(< (::a %%) (::b %%)))))
:user/f
user=> (s/explain-data ::f {::a 1 ::b 2})
nil
user=> (s/explain-data ::f {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 1, :b 0}, :via [:user/f], :in []}), :spec :user/f, :value #:user{:a 1, :b 0}}
0
_由 jcr 评论

我无法重现这些情况。

在问题描述中,断言 {{#(< (::a %%) (::b %%))}} 需要 a 小于 b,所以预期能不满足规格。

在第一条评论中,断言 {{a<b}} 是无效的,因为它接受两个数字而不是一个映射。

以下片段也按预期工作(匿名谓词、命名谓词以及在 s/spec 中包裹的谓词之间没有区别)

(s/def ::a number?)
(defn good? [m] (contains? m :a))

(def s1 (s/merge (s/keys :req-un [::a]) good?))
(def s2 (s/merge (s/keys :req-un [::a]) #(contains? % :a)))
(def s3 (s/merge (s/keys :req-un [::a]) (s/spec #(contains? % :a))))

(s/valid? s1 {:a 42}) ;=> true
(s/valid? s2 {:a 42}) ;=> true
(s/valid? s3 {:a 42}) ;=> true


我认为可以关闭这个问题。
0

由 dchelimsky 评论

在我更新亚历克斯评论后的例子时,我把消息放在了错误的位置。我刚刚再次更新了它,现在消息与输出正确对齐。jcr,我看到你的例子是有效的,但我仍然无法实现。所以这并不适用于所有匿名谓词,但仍然存在令人惊讶的行为。

说到s/merge文档说明它支持键规范,并未声称支持任何其他类型的谓词,所以如果你想基于这个理由将其关闭,那么这样做也是可以的。

0
_由 jcr 评论

啊,现在我明白你的意思了。抱歉我过于草率下结论。

因此,这里实际的问题是,使用不包含在s/spec调用中的谓词用作s/merge时,{{s/explain}}不一致地报告了根据{{s/conform}}有效的输入。以下是我设法制作的最小示例


(ns test
  (:require [clojure.spec.alpha :as s]))

(defn pred [m] (contains? m :a))
(s/def ::s (s/merge pred))

(def good {:a 42})
(def bad  {})

;; 如预期
(s/conform ::s good) ;=> {:a 42}
(s/conform ::s bad)  ;=> :clojure.spec.alpha/invalid

;; !!!: 第一个不应该失败
(s/explain-str ::s good) ;=> "val: {:a 42} fails spec: :test/s predicate: pred\n"
(s/explain-str ::s bad)  ;=> "val: {} fails spec: :test/s predicate: pred\n"

;; 明确在spec中包装pred
(s/def ::s* (s/merge (s/spec pred)))

;; 现在,如预期那样工作
(s/explain-str ::s* good) ;=> "Success!\n"
(s/explain-str ::s* bad)  ;=> "val: {} fails spec: :test/s* predicate: pred\n"


如果您同意,请相应地更新标题和描述。
0
_由 jcr 评论

据我所知,问题在于{{merge-spec-impl}}只是调用给定谓词的{{explain-1}},而{{explain-1}}总是返回问题,如果{{(spec? pred)}}为假;merge-spec-impl最可能应该在其谓词上调用{{specize}},类似它在{{tuple-impl}}中做的。


(let [specs (delay (mapv specize preds forms)) ...] ...)
0
参考:https://clojure.atlassian.net/browse/CLJ-2321(由dchelimsky报告)
...