clojure.spec

Uses: validation at system boundaries, documentation, destructuring with conform, generative testing, and macro syntax checking (Clojure itself specs ns, let, defn). spec2 (alpha) is an evolving redesign.

(require '[clojure.spec.alpha :as s]
         '[clojure.spec.gen.alpha :as gen]
         '[clojure.spec.test.alpha :as stest])

;; Specs for values
(s/def ::name (s/and string? #(not (clojure.string/blank? %))))
(s/def ::age (s/and int? #(<= 0 % 150)))
(s/def ::email (s/and string? #(re-matches #".+@.+\..+" %)))
(s/def ::role #{:admin :editor :viewer})            ; sets are predicates
(s/def ::roles (s/coll-of ::role :kind set?))

;; Specs for maps (by key names)
(s/def ::person (s/keys :req-un [::name ::age] :opt-un [::email ::roles]))

(s/valid? ::person {:name "Ana" :age 30})           ; true
(s/valid? ::person {:name "" :age 200})             ; false
(s/explain ::person {:name "" :age 200})            ; human-readable explanation
(s/explain-data ::person {:name "Ana"})             ; machine-readable
(s/conform ::age 30)                                ; 30 (or :clojure.spec.alpha/invalid)

;; Sequence specs (regex ops)
(s/def ::config (s/cat :host string? :port int? :flags (s/* keyword?)))
(s/conform ::config ["localhost" 8080 :debug :verbose])
;; {:host "localhost", :port 8080, :flags [:debug :verbose]}

;; Alternatives
(s/def ::id (s/or :num int? :str string?))
(s/conform ::id
…