Clojure has a new core library,
clojure.spec , that aims to provide a standard and integrated system for the specification and testing of data and functions, writes Rich Hickey, Clojure’s creator. Besides making it possible to automatically validate Clojure code, the new specification system can be also used for a number of tasks such as generative testing, error reporting, destructuring and more.
clojure.spec leverages the notion that specifications are the logical composition of predicates, such as
#(< 42 % 66) , through logical operators such as
spec/or . According to Hickey,
clojure.spec draws upon previous work done with contract systems, such as RDF and Racket’s , and others.
For example, a
map can be specified calling
(spec/keys :req [::x ::y (or ::secret (and ::user ::pwd))] :opt [::z])
Note that a
map specification does not support specifying what kind of values can be associated to a given keyword. Instead,
clojure.spec prefers- and to some extent enforces- the specification of values that can be associated to a namespace keyword under the keyword itself:
(spec/def ::x integer?) (spec/def ::y integer?) (spec/def ::z integer?)
Such value specifications are then applied in any map using those same keywords.
Sequences can be specified in
clojure.specs by means of regular expressions that describe how predicates match the sequence. Functions can be specified using three separate specs:
- one for the arguments, seen as a single list;
- one for the return, seen as a single value;
- an optional one for the relationship between arguments and return. Such a spec could require, for example, that all keys of an input map be present in the returned map.
You can get more details about how to define Clojure specification using
clojure.spec here .
Once you have a specification, you can use it by applying
conform to a value to validate it. In case
:clojure.spec/invalid , you can use
explain to find out why. Additionally, you can use
instrument to wrap a function so it tests its three specs. For testing, you can use
run-tests to run a suite of generative tests on an entire namespace; or,
gen to construct a
test.check compatible generator for a spec.
clojure.spec is not the first attempt at bringing specifications to Clojure. Previously, developers could use Schema and Herbert . A different approach to adding guarantees to Clojure is provided by
clojure.typed , a gradual typing library enforcing the concept of compile-time validation.
clojure.spec is available in version 1.9.1/alpha1 of Clojure. You can use it by including the following declaration in your
[org.clojure/clojure "1.9.0-alpha1"] .