| 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))))