I'm trying out Clojure spec and calling it from a function :pre
constraint.
My spec ::mustbe-seql-of-vec-of-2-int
shall check whether the argument passed to the function is a sequential of anything from 0 to 4 vectors of exactly 2 integers:
(require '[clojure.spec.alpha :as s])
(s/def ::mustbe-seql-of-vec-of-2-int
(s/and
::justprint
(s/coll-of
::mustbe-vec-of-2-int
:kind sequential?
:min-count 0
:max-count 4)))
So the spec is composed of other specs, in particular ::justprint
which does nothing except print the passed argument for debugging and coll-of to test the collection argument.
The doc for coll-of says:
Usage: (coll-of pred & opts)
Returns a spec for a collection of items satisfying pred. Unlike 'every', coll-of will exhaustively conform every value.
However, as first argument, I'm not using a pred (a function taking the argument-to-check and returning a truthy value) but another spec (a function taking the argument to check and returning I'm not sure what), in this case, the spec registered under ::mustbe-vec-of-2-int
.
This works perfectly well.
Is this correct style and expected to work?
P.S.
(s/def ::justprint
#(do
; "vec" to realize the LazySeq, which is not realized by join
(print (d/join [ "::justprint ▶ " (vec %) "\n"] ))
true))
(s/def ::mustbe-vec-of-2-int
(s/coll-of integer? :kind vector? :count 2))
Is this correct style and expected to work?
Yes. Specs, keywords that can be resolved to specs, and plain predicate functions can be used interchangeably in many parts of the clojure.spec API. "Pred" in the context of that docstring has a broader meaning than a general Clojure predicate function e.g. nil?
.
(require '[clojure.spec.alpha :as s])
(s/conform int? 1) ;; => 1
(s/conform int? false) ;; => :clojure.spec.alpha/invalid
s/def
modifies the clojure.spec registry, associating the keyword with the spec value. When you pass ::some-spec-keyword
to clojure.spec API it resolves the spec values themselves from the registry. You can also "alias" specs e.g. (s/def ::foo ::bar)
.
(s/def ::even-int? (s/and int? even?))
(s/conform ::even-int? 2) ;; => 2
(s/conform ::even-int? 3) ;; => :clojure.spec.alpha/invalid
So these are all equivalent:
(s/conform (s/coll-of (s/and int? even?)) [2 4 6 8])
(s/conform (s/coll-of ::even-int?) [2 4 6 8])
(s/def ::coll-of-even-ints? (s/coll-of ::even-int?))
(s/conform ::coll-of-even-ints? [2 4 6 8])