clojure-spec

Fil 2025-05-13T10:40:16.444529Z

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

Alex Miller (Clojure team) 2025-05-13T12:03:25.148679Z

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

Fil 2025-05-13T12:27:32.798029Z

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

Fil 2025-05-13T12:31:40.715429Z

I mean, I thought that if the first clause is true, it will not evaluate the second clause like

user=> (or :a :b)
:a

Alex Miller (Clojure team) 2025-05-13T13:08:21.741729Z

They do literally use and and or from core, but maybe you’re asking about something else, maybe you could give an example

Fil 2025-05-13T13:12:58.984979Z

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

Fil 2025-05-13T13:17:11.533029Z

I was expecting the or to work as a predicate like this.

(or (and (::x %) (::y %))
    (::x+y %))