(fn params ..body)
  params: an arr
  body: zero or more forms
  returns a fn
S
M

Defines a function.

When a fn form is evaluated, it allocates and returns a new closure (a value which belongs to the primitive type fn). Just like a Rust closure, this closure may capture any local variables which are in scope. Each local variable may be simultaneously captured by multiple closures.

(let counter (do
  (let n 0)
  (fn ()
    (inc! n)
    n)))

(prn (counter)) ; prints 1
(prn (counter)) ; prints 2
(prn n) ; an error: n is not in scope

If the same fn form is evaluated multiple times (for example, in a loop), it will allocate a new closure each time.

params must be a valid array pattern. When the function is called, all of its arguments are matched against params, and then its body forms are evaluated as an "implicit do". It's an error if the call's arguments do not match params - for example, because too few or too many arguments were provided.

(let f (fn (a b c) (prn a b c)))
(f 1 2) ; error: expected 3 or more arguments

The return or yield special forms can be used to return early from the function call. Otherwise, the result of the function call is the evaluation result of the body forms.

A fn form may specify one or more flags, listed immediately before params:

let
return