clojure 规范 - 验证地图的内容
clojure spec - validating contents of maps
我想为地图创建一个 clojure 规范,其中包含关于特定键存在的规则。
地图必须有 :type
,可以有 :default
或 :value
,但不能同时有。我试过了:
(s/def ::propertyDef
(s/keys :req [::type (s/or ::default ::value) ] :opt [::description ::required]))
但是我得到了
CompilerException java.lang.AssertionError: Assert failed:
spec/or expects k1 p1 k2 p2..., where ks are keywords
(c/and (even? (count key-pred-forms)) (every? keyword? keys)),
compiling:(C:\Users\MartinRoberts\AppData\Local\Temp\form-init4830956164341520551.clj:1:22)
但是 or
给了我一个错误,因为它的格式不对。我不得不承认在 s/or
.
的文档中并没有真正理解
首先:您正在使用 s/or
在所需键列表中指定 ::default
或 ::value
。 s/or
需要 :label spec
对,而您只给出规格本身,这就是错误的原因。
要解决,只需使用or
代替:
(s/def ::propertyDef (s/keys :req [::type (or ::default ::value)]
:opt [::description ::required]))
这允许 ::default
和 ::value
都出现在地图中,但这几乎总是可以的。实际使用地图的代码可以简单地检查 ::value
的存在并使用它,如果它不存在,则使用 ::default
(或者你的逻辑恰好是什么)。通常这样做:
(let [myvalue (or (::value mymap) (::default mymap))] ...)
地图中可能有数千个键,这不会影响您提取所需键的能力。这就是为什么规范不提供 built-in 方法来指定不应该在地图中的键,只提供指定哪些键 应该 存在的方法(即 :req
和 :req-un
在 s/keys
中)。想一想大多数 http 服务器的工作方式:您可以给它们无意义的 header 键和值,但它们不会拒绝为请求提供服务;他们只是忽略它们并 return 回应。
因此,您可能不需要强制只出现一个或另一个,但如果必须,您可以定义一个异或函数:
(defn xor
[p q]
(and (or p q)
(not (and p q))))
然后将其作为附加谓词添加到规范中:
(s/def ::propertyDef (s/and (s/keys :req [::type (or ::default ::value)]
:opt [::description ::required])
#(xor (::default %) (::value %))))
(s/valid? ::propertyDef {::type "type" ::default "default"})
=> true
(s/valid? ::propertyDef {::type "type" ::value "value"})
=> true
(s/valid? ::propertyDef {::type "type" ::default "default" ::value "value"})
=> false
我想为地图创建一个 clojure 规范,其中包含关于特定键存在的规则。
地图必须有 :type
,可以有 :default
或 :value
,但不能同时有。我试过了:
(s/def ::propertyDef
(s/keys :req [::type (s/or ::default ::value) ] :opt [::description ::required]))
但是我得到了
CompilerException java.lang.AssertionError: Assert failed:
spec/or expects k1 p1 k2 p2..., where ks are keywords
(c/and (even? (count key-pred-forms)) (every? keyword? keys)),
compiling:(C:\Users\MartinRoberts\AppData\Local\Temp\form-init4830956164341520551.clj:1:22)
但是 or
给了我一个错误,因为它的格式不对。我不得不承认在 s/or
.
首先:您正在使用 s/or
在所需键列表中指定 ::default
或 ::value
。 s/or
需要 :label spec
对,而您只给出规格本身,这就是错误的原因。
要解决,只需使用or
代替:
(s/def ::propertyDef (s/keys :req [::type (or ::default ::value)]
:opt [::description ::required]))
这允许 ::default
和 ::value
都出现在地图中,但这几乎总是可以的。实际使用地图的代码可以简单地检查 ::value
的存在并使用它,如果它不存在,则使用 ::default
(或者你的逻辑恰好是什么)。通常这样做:
(let [myvalue (or (::value mymap) (::default mymap))] ...)
地图中可能有数千个键,这不会影响您提取所需键的能力。这就是为什么规范不提供 built-in 方法来指定不应该在地图中的键,只提供指定哪些键 应该 存在的方法(即 :req
和 :req-un
在 s/keys
中)。想一想大多数 http 服务器的工作方式:您可以给它们无意义的 header 键和值,但它们不会拒绝为请求提供服务;他们只是忽略它们并 return 回应。
因此,您可能不需要强制只出现一个或另一个,但如果必须,您可以定义一个异或函数:
(defn xor
[p q]
(and (or p q)
(not (and p q))))
然后将其作为附加谓词添加到规范中:
(s/def ::propertyDef (s/and (s/keys :req [::type (or ::default ::value)]
:opt [::description ::required])
#(xor (::default %) (::value %))))
(s/valid? ::propertyDef {::type "type" ::default "default"})
=> true
(s/valid? ::propertyDef {::type "type" ::value "value"})
=> true
(s/valid? ::propertyDef {::type "type" ::default "default" ::value "value"})
=> false