clojure spec:包含a:with或a:height(XOR)的地图

Ant*_*ald 5 clojure clojure.spec

以下clojure规范::my允许具有键:width或key:height的映射,但是它不允许同时具有以下两者:

(s/def ::width int?)

(s/def ::height int?)

(defn one-of-both? [a b]
  (or (and a (not b))
      (and b (not a))))

(s/def ::my (s/and (s/keys :opt-un [::width ::height])
                   #(one-of-both? (% :width) (% :height))))
Run Code Online (Sandbox Code Playgroud)

即使它完成了这项工作:

(s/valid? ::my {})
false
(s/valid? ::my {:width 5})
true
(s/valid? ::my {:height 2})
true
(s/valid? ::my {:width 5 :height 2})
false
Run Code Online (Sandbox Code Playgroud)

代码看起来并不简洁.首先,将键定义为可选,然后根据需要定义.有没有人对此有更可读的解决方案?

Leo*_*hin 8

clojure.spec旨在鼓励能够增长的规格.因此它s/keys不支持禁止密钥.它甚至匹配具有既不在:req或不在的键的地图opt.

然而有一个方式来表达地图必须至少拥有:width :height,即不异,只是OR.

(s/def ::my (s/keys :req-un [(or ::width ::height)]))
Run Code Online (Sandbox Code Playgroud)


Ale*_*ler 5

此功能内置于规范中 - 您可以在 req-un 中指定和/或模式:

(s/def ::my (s/keys :req-un [(or ::width ::height)]))
:user/my
user=> (s/valid? ::my {})
false
user=> (s/valid? ::my {:width 5})
true
user=> (s/valid? ::my {:height 2})
true
user=> (s/valid? ::my {:width 5 :height 2})
true
Run Code Online (Sandbox Code Playgroud)