Clojureに新しいコアライブラリ、clojure.specが加わった。 Clojureの作者、Rich Hickeyによると、 このライブラリは、データおよび関数の仕様記述とテスティングに関する、標準的で統合されたシステムの提供を目的としている。 このライブラリは、Clojureコードの自動的な検証だけでなく、生成的テスティング、エラー報告、デストラクチャリング、その他様々なタスクに利用することができる。
clojure.specは、仕様の記法を改善する。 仕様記述には、述語(integer?や#(< 42 % 66)など)の論理結合が使用される。 clojure.specは、spec/andやspec/orといった、論理演算子を提供する。 Hickey氏によれば、clojure.specは、コントラクトシステム(contract systems)として知られる成果を元に開発されている。 コントラクトシステムは、RDFやRacket、その他にみられる。
たとえば、あるmapは、:reqと:optを引数として、keysを呼ぶものとして記述できる。
(spec/keys :req [::x ::y (or ::secret (and ::user ::pwd))] :opt [::z])
注目すべきは、このmapの仕様が、与えられたキーワードに対応する値の種類が何かを指定する方法を提供しないことだ。 直接指定する代わりに、clojure.specは、キーワード自身に紐付く名前空間キーワードに関連付けられた値の仕様を推奨- 時には強制- する。
(spec/def ::x integer?)
(spec/def ::y integer?)
(spec/def ::z integer?)
このような値の仕様記述は、同一のキーワードを使用するどのようなmapに対しても適用される。
clojure.specにおいては、シーケンスは、正規表現として仕様記述できる。 述語がどのようにシーケンスと一致するかを記述した正規表現として記述できるのだ。 関数は、3つの分離した仕様として記述できる。
clojure.specを使用したClojureの仕様記述に関する詳細は、ここを参照してほしい。
仕様を一度書けば、値に対してconformを適用することで、検証できるようになる。 もし、conformが:clojure.spec/invalidを返した場合は、explainにより、原因を探ることができる。 instrumentを使うと、関数を一つにまとめあげる。これにより、関数の持つ3つの仕様をテストできる。 テストのために、run-testsを利用できる。 これは、1つの名前空間に対して生成的テストスイートを実行する。 あるいは、genにより、1つの仕様のためのジェネレータを構築できる。 このジェネレータは、test.check互換である。
clojure.specは、Clojureに仕様記述を取り込む初の試み、というわけではない。 これまでにも、開発者たちは、SchemaやHerbertを使用してきた。 Clojureの動作を保証するための別のアプローチとしては、clojure.typedもある。 clojure.typedは、コンパイル時検証のコンセプトを実現するための、漸進的型付け(gradual typing)ライブラリである。
clojure.specを利用可能なClojureのバージョンは、1.9.0/alpha1以降である。 project.cljに次の記述を追加することにより、利用できるようになる。[org.clojure/clojure "1.9.0-alpha1"]