macro | since v0.0-927 | imported ![]() | Edit |
(fn & sigs)
Defines a function.
name?
is an optional name of the function to be used inside body
. This is
useful for recursive calls. Note that name?
in fn
is not the same as the
name
argument to defn
, which defines a global symbol for the function.
params*
are the arguments to the function and a binding form for the symbols
that the arguments will take inside the body of the function. Functions can have
arity of 0-20 and there is no runtime enforcement of arity when calling a
function (just like in JavaScript).
prepost-map?
is an optional map with optional keys :pre
and :post
that
contain collections of pre or post conditions
for the function.
body
is a series of expressions that execute when the function is called. The
arguments to the function are mapped to symbols in params*
and are available
in body
. The value of the last expression in body
is the return value of
calling the function.
params => positional-params* , or positional-params* & next-param positional-param => binding-form next-param => binding-form name => symbol Defines a function
(defmacro fn
{:added "1.0", :special-form true,
:forms '[(fn name? [params* ] exprs*) (fn name? ([params* ] exprs*)+)]}
[& sigs]
(let [name (if (symbol? (first sigs)) (first sigs) nil)
sigs (if name (next sigs) sigs)
sigs (if (vector? (first sigs))
(list sigs)
(if (seq? (first sigs))
sigs
;; Assume single arity syntax
(throw (IllegalArgumentException.
(if (seq sigs)
(str "Parameter declaration "
(first sigs)
" should be a vector")
(str "Parameter declaration missing"))))))
psig (fn* [sig]
;; Ensure correct type before destructuring sig
(when (not (seq? sig))
(throw (IllegalArgumentException.
(str "Invalid signature " sig
" should be a list"))))
(let [[params & body] sig
_ (when (not (vector? params))
(throw (IllegalArgumentException.
(if (seq? (first sigs))
(str "Parameter declaration " params
" should be a vector")
(str "Invalid signature " sig
" should be a list")))))
conds (when (and (next body) (map? (first body)))
(first body))
body (if conds (next body) body)
conds (or conds (meta params))
pre (:pre conds)
post (:post conds)
body (if post
`((let [~'% ~(if (< 1 (count body))
`(do ~@body)
(first body))]
~@(map (fn* [c] `(assert ~c)) post)
~'%))
body)
body (if pre
(concat (map (fn* [c] `(assert ~c)) pre)
body)
body)]
(maybe-destructured params body)))
new-sigs (map psig sigs)]
(with-meta
(if name
(list* 'fn* name new-sigs)
(cons 'fn* new-sigs))
(meta &form))))