Is it in some docs pages where it is described that the or operation in s/keys operates as an OR logic operation instead of XOR?
I found https://stackoverflow.com/questions/41899511/clojure-spec-map-containing-either-a-with-or-a-height-xor/41901585#41901585 Stack Overflow thread that explicitly states this, but I couldn't capture that information from other docs/guides. in docstrings, for example, it says:
The :req key vector supports 'and' and 'or' for key groups:
At first, I was expecting that to behave like clojure.core/or
I don’t think it says that because that’s what or means. If you want xor, you will need to s/and a predicate with that constraint
Yeah, I did that. I think I was confused because I assumed that support for 'and' ad 'or' would be like the ones from clojure.core
I mean, I thought that if the first clause is true, it will not evaluate the second clause like
user=> (or :a :b)
:aThey do literally use and and or from core, but maybe you’re asking about something else, maybe you could give an example
I've replied to the Stack Overflow thread with the scenario and approach I took.
Copied from there
---------------------
It could be a scenario where you need to specify a set keys of keys that must be present only if another set of keys is absent. For example, given a set #{::x, ::y} and another #{::x+y}
(require '[clojure.spec.alpha :as s])
(require '[clojure.spec.gen.alpha :as sgen])
(s/def ::x int?)
(s/def ::y int?)
(s/def ::x+y int?)
Determining the XOR predicate and the spec to generate a sample.
(defn- xor-contains?
[coll arg1 arg2]
(assert (set? arg1))
(assert (set? arg2))
(let [;; empty sets are nil
r1 (seq (clojure.set/select arg1 (set (keys coll))))
r2 (seq (clojure.set/select arg2 (set (keys coll))))]
(or (and r1 (not r2))
(and r2 (not r1)))))
(clojure.pprint/pprint
(sgen/sample
(s/gen
(s/and (s/keys :req [(or ;; NOTICE: because 'or' is not exclusive, need additional
;; predicate to enforce exclusiveness.
(and ::x ::y)
::x+y)])
#(xor-contains? % #{::x ::y} #{::x+y})))))
It will always gen a map with an exclusive set of keys.
user=>
(#:user{:x -1, :y 0}
#:user{:x 0, :y 0}
#:user{:x -1, :y -1}
#:user{:x+y 0}
#:user{:x+y -2}
#:user{:x -1, :y 0}
#:user{:x -4, :y -4}
#:user{:x 1, :y 0}
#:user{:x+y -18}
#:user{:x 125, :y 7})
I was expecting the or to work as a predicate like this.
(or (and (::x %) (::y %))
(::x+y %))