BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Clojure.spec is a New Contract System for Clojure

Clojure.spec is a New Contract System for Clojure

Bookmarks

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 integer? or #(< 42 % 66), through logical operators such as spec/and and 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 keys with :req and :opt arguments:

(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 conform returns :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 project.clj: [org.clojure/clojure "1.9.0-alpha1"].

Rate this Article

Adoption
Style

BT