Skip to content

Instantly share code, notes, and snippets.

@luxbock
Created January 27, 2017 08:28
Show Gist options
  • Save luxbock/43e189db5e014a478820b19742319399 to your computer and use it in GitHub Desktop.
Save luxbock/43e189db5e014a478820b19742319399 to your computer and use it in GitHub Desktop.
Handy helper for when it comes to playing around with :fn predicates at the REPL
(defn- get-fspec-attr
[sym attr]
(->> (s/form sym)
(rest)
(partition 2)
(keep (fn [[a b]] (when (= a attr) b)))
(first)))
(s/fdef conform-fn-call
:args (s/cat :fn-call (s/spec (s/cat :f symbol? :rs (s/* any?))))
:ret (s/or :invalid s/invalid?
:conf-map (s/keys :req-un [::args ::ret])))
(defmacro conform-fn-call
"Evaluates `form` and conforms its arguments and return value based
on the fspec of `f`, i.e. the return value is in the same shape as
what the specs/predicates of `:fn` expect."
[[f & args :as form]]
(let [f-res (-> f resolve str (subs 2) symbol)
ret (eval form)
args-spec (get-fspec-attr f-res :args)
ret-spec (get-fspec-attr f-res :ret)
args-conf (when args-spec `(s/conform ~args-spec ~(vec args)))
ret-conf (when ret-spec `(s/conform ~ret-spec ~ret))]
`(let [args# ~args-conf
ret# ~ret-conf]
(if (or (s/invalid? args#) (s/invalid? ret#))
::s/invalid
{:args (or args# ~(vec args))
:ret (or ret# ~ret)}))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment