在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_

当引用谓词函数时,它也总是失败,因此使它工作的唯一方法是定义另一个spec并引用它。


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 投票
by

评论者:alexmiller

((< (::a % ::b %)))看起来不正确?#(< (::a %) (::b %))是否工作正常?

0 投票
by

评论者:dchelimsky

好的捕捉,然而,没有任何#(< (::a _) (::b _))能在行内工作。我已经更新了示例来反映这一点。

0 投票
by

评论者:dchelimsky

此外,FWIW,我在打字错误之前已经缩小了问题范围。只是碰巧得到了带和不含打字错误的相同答案。

0 投票
by
评论由: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 投票
by
_评论者:jcr_

我无法复现这些问题。

在问题描述中,谓词{{#(< (::a _) (::b _))}}要求a小于b,所以预期{{{::a 1 ::b 0}}}不会匹配规范。

在第一条评论中,谓词{{a
以下片段也按预期工作(匿名谓词、命名谓词和在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

在Alex评论后更新示例时,我将消息放在了错误的位置。我再次更新了它,使信息与输出正确对齐。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"

;; 明确包裹pred在spec中
(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}}提供给preds,并且{{explain-1}}总是在{{(spec? pred)}}为false时返回问题;merge-spec-impl可能应该在其preds上调用{{specize}},类似于在{{tuple-impl}}中执行的方式。


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