Skip to content

Instantly share code, notes, and snippets.

@ohpauleez
Last active December 11, 2015 01:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ohpauleez/4527256 to your computer and use it in GitHub Desktop.
Save ohpauleez/4527256 to your computer and use it in GitHub Desktop.
An example of specifications-as-a-value.
(ns sterling.specs-example
(:require [clojure.core.specs :as spec]))
(comment
;; A preview of some Specifications.
;; Out of the box, a single spec can be used with:
;; test.generative, core.contracts, Typed Clojure, and Sterling (an interface to Alloy)
;; You're also free to extend the system however you need.
;; Here is an example of a simple specification.
;; Under the hood, specs are just maps. `raw-spec` just returns the map
(def inc-spec
(spec/raw-spec
inc
"A simple spec for non-negative inc" ; Optional docstring
'[^long x]
:constraints '[["The input is non-negative and always result in a positive number"
(not (neg? x)) => (pos? %)]]))
;; The map looks like this:
{:clojure.core.specs/type :defspec,
:clojure.core.specs/f #<core$inc clojure.core$inc@3970a4fb>,
:clojure.core.specs/args [x],
:clojure.core.specs/ext {:constraints [["The input is non-negative and always result in a positive number" (not (neg? x)) => (pos? %)]]
:typed [[Number -> Any]]},
:clojure.core.specs/doc "A simple spec for non-negative inc"}
; You'll notice type information was added...
; Specifying type information is optional. It'll be used if supplied,
; otherwise core.specs will attempt match types based on :tag information
;; These next two are the same.
;; Testing distributions can be "enforced" and used a contracts/constraints
(spec/raw-spec
inc
"An inc that only works for inputs 0-49"
'[^{:uniform [0 49] :enforced true} x]
:constraints '[["The output will always be less than or equal to 50"
(=> (<= % 50))]]
:typed '[[Number -> Number]])
(spec/raw-spec
inc
"An inc that only works for inputs 0-49"
'[^{:tag (uniform 0 49)} x]
:constraints '[["The input must be between 0-49"
;(and (>= x 0) (<= x 49))
(spec/between 0 <= x <= 49)]
["The output will always be less than or equal to 50"
(=> (<= % 50))]]
:typed '[[Number -> Number]])
;; The specs can be used like decorators, generating the constrained
;; forms of the function detailed in the spec.
;; The following generates a new function with the constraints and type
;; information added.
;;
;; (def constrained-fn (spec/with a-raw-spec :constraints :typed))
;; Here is an example from our earlier spec...
(def pos-inc (spec/with inc-spec :constraints))
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment