Skip to content

Instantly share code, notes, and snippets.

@rplevy
Last active June 11, 2016 05:13
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 rplevy/6549315a8f3fe5aea10290ed12b07ff9 to your computer and use it in GitHub Desktop.
Save rplevy/6549315a8f3fe5aea10290ed12b07ff9 to your computer and use it in GitHub Desktop.
temporal higher-order contract with clojure.spec
(require '[clojure.spec :as s])
(defn enforce [spec data-value]
(let [r (s/conform spec data-value)]
(if (= r :clojure.spec/invalid)
(throw (ex-info (str r) (s/explain-data spec data-value)))
data-value)))
(defn call-with-temporal-contract [f g contract-on-g]
(let [function-calls (atom [])]
(let [g' (fn [& [arg]]
(swap! function-calls conj arg)
(enforce contract-on-g @function-calls)
(g arg))]
(f g'))))
;; valid
(call-with-temporal-contract (fn [g] (g "foo") (g "bar") (g "baz"))
prn
#(< (count %) 4))
;; throws because prn was called three times
(call-with-temporal-contract (fn [g] (g "foo") (g "bar") (g "baz"))
prn
#(< (count %) 3))
;; valid because arguments of calls matched specified temporal pattern
(call-with-temporal-contract (fn [g] (g "foo") (g :baz) (g 1))
prn
(s/cat :call1 string?
:call2 (s/? keyword?)
:call3 (s/? integer?)))
;; throws because arguments in sequence of calls did not match pattern
(call-with-temporal-contract (fn [g] (g "foo") (g :baz) (g :bar))
prn
(s/cat :call1 string?
:call2 (s/? keyword?)
:call3 (s/? integer?)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment