Let's start with a regular sequence
(require '[clojure.spec :as spec]
'[clojure.spec.gen :as gen])
(spec/def ::cat (spec/cat :sym symbol? :str string? :kws (spec/* keyword?)))
which corresponds to vectors
(spec/conform ::cat '[af "5"])
=> {:sym af, :str "5"}
(spec/conform ::cat '[af "5" :key])
=> {:sym af, :str "5", :kws [:key]}
but also lists
(spec/conform ::cat '(af "5"))
=> {:sym af, :str "5"}
(spec/conform ::cat '(af "5" :key))
=> {:sym af, :str "5", :kws [:key]}
If we want to limit this, we can try to use spec/tuple; but, unfortunately, it corresponds only to vectors of a fixed length, i.e. so that at least the empty list is the last part of the tuple:
(spec/def ::tuple (spec/tuple symbol? string? (spec/* keyword?)))
(spec/conform ::tuple '[af "5"])
=> :clojure.spec/invalid
(spec/exercise ::tuple)
=> ([[r "" ()] [r "" []]] [[kE "" (:M)] [kE "" [:M]]] ...)
We can also try to add an additional condition to ::catwith spec/and:
(spec/def ::and-cat
(spec/and vector? (spec/cat :sym symbol? :str string? :kws (spec/* keyword?))))
which corresponds to a fine
(spec/conform ::and-cat '[af "5"])
=> {:sym af, :str "5"}
(spec/conform ::and-cat '[af "5" :key])
=> {:sym af, :str "5", :kws [:key]}
(spec/conform ::and-cat '(af "5" :key))
=> :clojure.spec/invalid
but, unfortunately, it is not possible to create your own data, because the generator for spec/catcreates only lists, which, of course, will not correspond to the predicate vector?:
(spec/exercise ::and-cat)
=> Couldn't satisfy such-that predicate after 100 tries.
, : , [hi "there"] [my "dear" :friend]?
" spec/cat, ?" " : kind spec/cat?"? " , ?".