Skip to content

Differences from spec.alpha

Alex Miller edited this page Jan 21, 2019 · 20 revisions

An overview of the differences between spec.alpha and spec-alpha2.

Creating Specs with Spec Ops

  • Specs created with spec or regex ops (and, or, tuple, cat, etc) must now be only data (symbols, sets, keywords, and lists), no function objects

Calling the Spec API

  • As mentioned above, the spec API functions can no longer be invoked with things that evaluate to a function object, only with data that defines a spec.
    • Symbols can't be used directly but can be quoted:
      • (s/conform 'int? 10)
    • Anonymous functions can't be used directly but can be quoted:
      • (s/conform '#(> % 10) 100)
    • Functions that return a spec object can be wrapped in an anonymous function and quoted:
      • (s/conform '#((seqable-of int?) %) [100])

Creating Specs Programmatically

The new s/spec* functional entry point can be used to construct spec objects from spec forms without invoking the spec op macros or using eval:

(require '[clojure.spec-alpha2 :as s])

(defn make-keys-spec [keys]
  (s/spec* (list `s/keys :req (vec keys))))

(s/explain (make-keys-spec #{::a ::b}) {::a 1})
;; #:user{:a 1} - failed: (contains? % :user/b)

Implementing Custom Specs

Custom spec ops can now be created and installed with a two-step process. First, create a spec op macro that explicates (fully-qualifies) a form and invokes the functional interface:

(defmacro my-spec
  [& opts]
  `(s/spec* '~(s/explicate (ns-name *ns*) `(my-spec ~@opts))))

Second, calls to spec* get routed (via the spec name) to the multimethod create-spec that actually creates the spec by reifying the Spec protocol:

(defmethod create-spec 'my-ns/my-spec
  [[_ & opts]]
  (reify Spec
    ...))
Clone this wiki locally