Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clojure spec: map containing either a :with or a :height (XOR)

The following clojure spec ::my permits maps having either the key :width or the key :height, however it does not permit having both of them:

(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))))

Even if it does the job:

(s/valid? ::my {})
false
(s/valid? ::my {:width 5})
true
(s/valid? ::my {:height 2})
true
(s/valid? ::my {:width 5 :height 2})
false

the code does not appear that concise to me. First the keys are defined as optional and then as required. Does anyone have a more readable solution to this?

like image 826
Anton Harald Avatar asked Jan 27 '17 17:01

Anton Harald


1 Answers

clojure.spec is designed to encourage specs capable of growth. Thus its s/keys does not support forbidding keys. It even matches maps having keys that are neither in :req or opt.

There is however a way to say the map must at least have :width or :height, i.e. not XOR, just OR.

(s/def ::my (s/keys :req-un [(or ::width ::height)]))
like image 83
Leon Grapenthin Avatar answered Oct 02 '22 13:10

Leon Grapenthin