Unleashing the Power of clojure.spec: Insights from Clojure/conj 2016
Table of Contents
Background
clojure.spec received significant attention at Clojure/conj 2016 including:
- Halloway's example for ETL,
- Rohner's Spectrum,
- Composing music with clojure.spec,
- Normand's drawing examples, and
- underpinings in Hickey's keynote.
Setup
Background on the rationale of clojure.spec is noted at https://clojure.org/about/spec . As noted it provices composability of entities with definitions similar to
Examples
Basic Specs
(require '[clojure.spec.alpha :as s]) ;; Define a spec for a non-empty string (s/def ::name (s/and string? #(> (count %) 0))) ;; Define a spec for an email (s/def ::email (s/and string? #(re-matches #".+@.+\..+" %))) ;; Define a spec for age (positive integer) (s/def ::age (s/and int? pos?)) ;; Composite spec for a person (s/def ::person (s/keys :req [::name ::email] :opt [::age])) ;; Validation (s/valid? ::person {::name "Alice" ::email "alice@example.com"}) ;; => true (s/explain ::person {::name "" ::email "invalid"}) ;; => fails with explanation
Generative Testing
(require '[clojure.spec.gen.alpha :as gen]) ;; Generate sample data (gen/sample (s/gen ::name) 5) ;; => ("a" "xy" "abc" "test" "hello") ;; Generate valid persons (gen/generate (s/gen ::person)) ;; => {::name "xyz" ::email "a@b.com" ::age 42}
Function Specs
(defn greet [person] (str "Hello, " (::name person) "!")) (s/fdef greet :args (s/cat :person ::person) :ret string?) ;; Instrument for development (require '[clojure.spec.test.alpha :as stest]) (stest/instrument `greet)