(u/sci-macro defgeneric
"Defines a multifn using the provided symbol. Arranges for the multifn
to answer the :arity message, reporting either `[:exactly a]` or
`[:between a b]` according to the arguments given.
- `arities` can be either a single or a vector of 2 numbers.
The `options` allowed differs slightly from `defmulti`:
- the first optional argument is a docstring.
- the second optional argument is a dict of metadata. When you query the
defined multimethod with a keyword, it will pass that keyword along as a query
to this metadata map. (`:arity` is always overridden if supplied, and `:name`
defaults to the symbol `f`.)
Any remaining options are passed along to `defmulti`."
{:arglists '([name arities docstring? attr-map? & options])}
[f arities & options]
(let [[a b] (if (vector? arities) arities [arities])
arity (if b [:between a b] [:exactly a])
docstring (if (string? (first options))
(str "generic " f ".\n\n" (first options))
(str "generic " f))
options (if (string? (first options))
(next options)
options)
[attr options] (if (map? (first options))
[(first options) (next options)]
[{} options])
kwd-klass (fork :clj Keyword :cljs 'cljs.core/Keyword)
attr (assoc attr
:arity arity
:name (:name attr `'~f))]
`(do
(defmulti ~f
~docstring
{:arglists '~(arglists a b)}
v/argument-kind ~@options)
(defmethod ~f [~kwd-klass] [k#]
(~attr k#)))))