spec-backed forms of defn/defprotocol/..., aided by metadata

License: EPL-2.0

Language: Clojure

speced.def CircleCI

This library provides spec-backed forms of defn, defprotocol, fn, etc. using the same exact syntax than clojure.core's.

That way, you can strengthen your defns with custom specs (expressed as metadata), while avoiding the hassle of instrumentation, and gaining some extra benefits, such as better performance, error reporting, etc.



[com.nedap.staffing-solutions/speced.def "1.0.1"]

Note that self-hosted ClojureScript (e.g. Lumo) is unsupported at the moment.

Production setup

clojure-future-spec incompatibility

If your project happens to depend (directly or transitively; try e.g. lein deps :tree) on clojure-future-spec, this library will fail to load.

You can fix that by adding e.g. :exclusions [clojure-future-spec] to whatever dependency was bringing it in.

You also will need to create an empty clojure.future ns.


With speced.def, one expresses specs via metadata:

(spec/def ::int int?)

;; You can pass functions, keywords, or (primitive) type hints indifferently, as metadata:
(speced/defn ^string? inc-and-serialize [^::int n, ^boolean b]
  (-> n inc str))

...the snippet above compiles to something akin to:

;; note that both preconditions and type hints are emitted, derived from the specs
(defn inc-and-serialize ^String [n, ^boolean b]
  {:pre [(int? n) (boolean? b)]
   :post [(string? %)]}
  (-> n inc str))

Expound is used for error reporting, so the actual emitted code is a bit more substantial.

You can pass specs as part of any nested destructurings.

(speced/defn destructuring-example [{:keys [^string? a] :as ^::thing all}]
  ;; :pre conditions will be emitted for `a` and `all`

speced.def's philosophy is to bypass instrumentation altogether. Clojure's precondition system is simple and reliable, and can be cleanly toggled for dev/prod environments via the clojure.core/*assert* variable.

In a future, we might provide a way to build your own defn, tweaking subjective aspects like instrumentation, while preserving all other features at no cost.


  • 1:1 syntax mapping to clojure.core's
    • No IDE pains, nothing new to learn, trivial upgradeability from old defns
    • Things like N-arities are supported.
  • Multiple metadata-based 'syntaxes'
  • Type hints become specs
    • e.g.^Boolean x is analog to ^boolean? x
    • emits safe and efficient code, addressing an old complaint about Clojure's type hinting mechanism ("it doesn't enforce types").
    • You can ^::speced/nilable ^Boolean x if something can be nil.
  • Inline function specs can become type hints
    • e.g. ^string? will emit a ^String type hint
    • same for ClojureScript: ^string? -> ^string
    • This particularly matters in JVM Clojure. Refer to the full mapping.
  • speced/defprotocol, speced/fn are also offered
    • Same syntax and advantages as speced/defn
  • Richer Expound integration
    • This idea is implemented here inline, as long as the Expound issue remains open.
  • Full ClojureScript support
    • Did you know Clojure and ClojureScript expect type hinting metadata at different positions?
      • speced.def abstracts over that, allowing you to write them wherever you please.
  • speced/def-with-doc: clojure.spec.alpha/def with a docstring
    • It will show up in speced/doc, along with the spec itself
    • It also will show up in REBL.

Status and roadmap

  • Battle-tested across a variety of projects by now
    • Rough edges polished at this point, each bugfix being thoroughly unit-tested.
    • ClojureScript support is full (and fully unit-tested), but less battle-tested.
  • Richer features will be added
    • Refer to the issue tracker for getting an idea of the project's wishlist.

ns organisation

There are exactly 2 namespaces meant for public consumption:

  • nedap.speced.def: 'speced' forms of defprotocol, defn, fn, etc.
  • nedap.speced.def.doc: a public docstring registry for specs. Can be queried from arbitrary tools, and particularly REBL.

They are deliberately thin so you can browse them comfortably.


Please browse the public namespaces, which are documented, speced and tested.

It is part of this library's philosophy to avoid creating external documentation. We believe specs, particularly with our def-with-doc helper, can suffice, preventing duplication/drift and non-speced/non-self-documenting code.

That assumes that adopters are willing to jump to the source (particularly to the public parts, and occasionally to the tests which serve as a large corpus of examples), and preferrably have handy tooling that is able to do such jumping.



Copyright © Nedap

This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at

Project Statistics

Sourcerank 5
Repository Size 271 KB
Stars 23
Forks 1
Watchers 4
Open issues 21
Dependencies 3
Contributors 4
Tags 19
Last updated
Last pushed

Top Contributors See all

vemv Jeroen de Jong jwkoelewijn Lennart Buit

Packages Referencing this Repo

spec-backed forms of `defn`, `defprotocol`, `fn`, etc, using the same exact syntax than clojure.c...
Latest release 1.1.0-alpha2 - Updated - 23 stars

Recent Tags See all

v1.0.1 September 05, 2019
v0.9.1-alpha1 August 27, 2019
v1.1.0-alpha2 August 14, 2019
v1.1.0-alpha1 August 14, 2019
v1.0.0 July 26, 2019
v1.0.0-alpha5 July 25, 2019
v1.0.0-alpha3 July 25, 2019
v1.0.0-alpha2 July 25, 2019
v1.0.0-alpha1 July 25, 2019
v0.9.0 July 10, 2019
v0.8.2 June 07, 2019
v0.8.1 June 06, 2019
v0.8.0 June 06, 2019
v0.7.1-alpha2 May 27, 2019
v0.7.1-alpha1 May 27, 2019

Something wrong with this page? Make a suggestion

Last synced: 2019-09-05 15:28:37 UTC

Login to resync this repository