defn-spec lets you create Clojure Specs inline with your defn
. The syntax (and implementation) has been borrowed from Schema, so if you've used that before, this should be very familiar.
You can define your argument and return specs inline with your defn
:
(ns my.ns
(:require [net.danielcompton.defn-spec-alpha :as ds]
[clojure.spec.alpha :as s]
; ...
))
;; Predicate definitions elided for brevity
(s/def ::instant instant?)
(s/def ::zone-id zone-id?)
(s/def ::zoned-date-time zoned-date-time?)
(ds/defn to-zoned-dt :- ::zoned-date-time
[instant :- ::instant
zone-id :- ::zone-id]
(ZonedDateTime/ofInstant instant zone-id))
Instead of writing your fdef
separately from your defn
:
(defn to-zoned-dt
[instant zone-id]
(ZonedDateTime/ofInstant instant zone-id))
(s/fdef to-zoned-dt
:args (s/cat :instant ::instant :zone-id ::zone-id)
:ret ::zoned-date-time)
Add the defn-spec-alpha dependency to your project's dependencies. Don't add it to your development dependencies unless you are only speccing development time functions.
Leiningen/Boot
[net.danielcompton/defn-spec-alpha "0.1.0"]
deps.edn
{:deps
{net.danielcompton/defn-spec-alpha {:mvn/version "0.1.0"}}}
Note that clojure.spec is required to use defn-spec. This requires Clojure 1.9.0 or greater.
The basic syntax is the same as clojure.core/defn
, but you can optionally add 'spec hints' with :-
to any of the arguments and the return value. Any valid spec can be used, e.g. functions, sets, registered spec identifiers.
(ds/defn my-fn-name :- <:ret spec>
[arg1 :- <spec for single arg>
arg2 :- <spec for single arg>
arg3] ;; Not all args need to be specced
; ...
)
I've been using Clojure spec for a while, but I found that I often resisted writing specs for functions. This was mostly because I didn't want to have to duplicate a bunch of information into the fdef
. It's not a huge deal, but in my experience it was enough to deter me from writing specs while I was heavily working on an area of code. I created defn-spec to increase the locality of the spec definitions, and to reduce the activation energy to start adding specs to your codebase.
This is similar to Orchestra's defn-spec macro, but allows for optionally only speccing part of the function, and matches the well-known Schema defn syntax. Try them both out though, and see which one works best for you.
Like all things in life, defn-spec has benefits and tradeoffs:
Benefits
s/cat
form.Tradeoffs
:args
specs with many branches, it may be simpler to define the fdef
separately. defn-spec
is designed for the 80-90% of Clojure functions that have simple argument lists and return types.fdef
is defined separately, it forces you to think more about growing the spec.:ret
spec as-is, but constructs an :args
spec for you based on the function's argument names and the 'spec hints' that you provide.defn
does. If you spot anything that isn't being retained, this is a bug, let me know!ds/defn
macro, but don't define any 'spec hints' for the arguments or return value then no fdef
spec is defined.ds/defn
macro, the fdef
is defined immediately after the clojure.core/defn
. If you declare fdefs after this point, they will overwrite the defn-spec fdef
. You cannot merge 'spec hints' defined on a function and other spec definitions in a standalone fdef
.:args
, :fn
, and :ret
specs for your fdef
's.& rest
, & [a b]
, and :keys
destructuring are not yet supported. #3, #4, #10:fn
specs are not supported yet, as I'm not sure where to put the :fn
spec yet. #6defn
macro.This library is currently in alpha preview and is soliciting feedback on functionality and syntax from interested parties before publishing an official release. In the meantime, you can use a SNAPSHOT build at [TODO]
.
defn-spec-alpha follows clojure.spec.alpha. When clojure.spec-alpha2 is released, the plan is to publish a new artifact ID and set of defn-spec-alpha2
namespaces, so you can use both versions side-by-side as you migrate to spec-alpha2.
Long-term I would like defn-spec
to be so stable that it is safe to include as a library dependency. While I strongly want to keep source compatibility, I can't guarantee this in the short-term. Until this warning is removed I would recommend only using this in applications or libraries where you control all of the consumers. There have also been rumblings that eventually there may be something similar to this built into Clojure's core defn macro.
You can tell Cursive to resolve defn-spec's defn
macro like the schema defn
macro. See the Cursive setup page for full details on how to do this.
Copyright © 2017-2019 Daniel Compton
defn-spec contains extensive copying from plumatic/schema, and couldn't have been completed without their great work.
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close