Fork me on GitHub

Is there a catenation which doesn't care about order? I have specs ::a, ::b, ::c and ::d. I want a catenation in which ::a and ::b are required, ::c and ::d are optional and order doesn't matter. Is this possible with spec?

Alex Miller (Clojure team)12:09:59

(s/keys* :req [::a ::b] :opt [::c ::d])

💡 4

^ does keys work on non-maps?

Alex Miller (Clojure team)12:09:33

s/keys* works on sequential collections

Alex Miller (Clojure team)12:09:17

user=> (require '[clojure.spec.alpha :as s])
user=> (s/def ::a int?)
user=> (s/def ::b string?)
user=> (s/def ::c keyword?)
user=> (s/def ::d symbol?)
user=> (s/def ::s (s/keys* :req [::a ::b] :opt [::c ::d]))
user=> (s/conform ::s [::b "abc" ::c :wow ::a 100])
#:user{:b "abc", :c :wow, :a 100}
user=> (pprint (take 5 (s/exercise ::s)))
([(:user/a 0 :user/b "" :user/d q :user/c :T)
  #:user{:a 0, :b "", :d q, :c :T}]
 [(:user/a -1 :user/b "" :user/d !D/- :user/c :oh)
  #:user{:a -1, :b "", :d !D/-, :c :oh}]
 [(:user/a 1 :user/b "kK") #:user{:a 1, :b "kK"}]
 [(:user/a -4 :user/b "3" :user/d Jw/R5 :user/c :?jH)
  #:user{:a -4, :b "3", :d Jw/R5, :c :?jH}]
 [(:user/a 0 :user/b "Y6" :user/d NS.+_.H/-)
  #:user{:a 0, :b "Y6", :d NS.+_.H/-}])


there is a way to do exactly what keys* does but using quoted symbols instead of keywords? ex:

(s/conform ::foo [:source1 '-> :target1 'guard #() 'action #()])
Where the 'guard and the 'action maybe defined or not but the :source1 '-> :target1 should be required.

Alex Miller (Clojure team)17:09:08

keys* is capturing the common Clojure idiom of kwargs passed to a function (but those are always keywords)

Alex Miller (Clojure team)17:09:36

I would probably use regex for that

Alex Miller (Clojure team)17:09:56

(s/cat :s keyword? :_ #{'->} :t keyword? (s/* (s/alt :guard-pair (s/cat :_ #{'guard} :g any?) :action-pair (s/cat :_ #{'action} :a any?))))

👍 4
Alex Miller (Clojure team)17:09:10

or could be simpler if you want to spec less of the options (s/cat :s keyword? :_ #{'->} :t keyword? (s/* (s/cat :op symbol? :val any?)))


@U064X3EF3 thank you sir! picard-facepalm I should have perused the clojure.spec.alpha docs.


oh wow didn't know that - but I suppose it makes sense for s/keys to work like that


You could probably do the s/or of all the possible orders, but that would be pretty tedious and error prone.


Why do you want a catenation if the order doesn't matter? Are these args to a function?


Trying to implement serialization and deserialization of a protocol using conform and unform.


To see just how much of it can be pushed into spec? This is the last step.


Actual use case contains ~20 specs, so s/oring 20 factorial orders is out of the picture.


I think I keep asking roughly the same question in different contexts but hoping someone can help me understand the best way to approach this: I need to programmatically create a spec based on external data (specifically, a GraphQL schema/query). Is there any good examples out there of how to approach this?


I think two current options depending on type of spec are either use spec's internals or eval, and I think spec-tools lib has some features for this


We've done this with code-generation


build up quoted lists of code then write it out to a clj file




if you have a deterministic order of evaluation (e.g. sorting the specs by name or topology or something) you will also see diffs clearly