Skip to content

Instantly share code, notes, and snippets.

@borkdude
Last active January 7, 2022 13:34
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save borkdude/8d69f326b3d363a0b34e7b949b039992 to your computer and use it in GitHub Desktop.
Save borkdude/8d69f326b3d363a0b34e7b949b039992 to your computer and use it in GitHub Desktop.
Use a spec for defining function arguments and get validation automatically
(ns specfn.core
(:require [clojure.spec :as s]
[clojure.spec.test :as t]))
(defn- spec-symbols [s]
(->> s
(drop 1)
(partition-all 2)
(map first)
(map name)
(mapv symbol)))
(defmacro defn* [name spec & body]
(let [params (spec-symbols spec)
spec-key (keyword (str *ns*) (str name "__args"))]
`(do
(s/def ~spec-key ~spec)
(defn ~name ~params
(if (s/valid? ~spec-key ~params)
~@body
(throw (ex-info
(s/explain-str ~spec-key ~params)
(s/explain-data ~spec-key ~params))))))))
(comment
(defn* my-fn (s/cat :n number? :s string?)
(str n s))
(my-fn 10 "bar") ;; => "10bar"
(my-fn 10 10)) ;; => clojure.lang.ExceptionInfo: In: [1] val: 10 fails spec: :specfn.core/my-fn__args at: [:s] predicate: string?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment