Clojure programs are made of expressions. There are no statements and no declarations: every form that is not handled by a special form or a macro is an expression that is evaluated to a value, and the same single-object rule applies everywhere (at the REPL, in load, and in eval). There is no separate compile step and no interpreter: anything that needs compiling is compiled to JVM bytecode when it is evaluated.
What each kind of form evaluates to
| Form | Value |
|---|---|
string, number, character, true, false, nil, keyword | itself |
| vector, set, map | a collection of the same kind holding the evaluated contents. Vector elements are evaluated left to right; sets and maps in an undefined order. Metadata maps are evaluated too |
() | the empty list |
| non-empty list | a call: to a special form, to a macro or to a function |
| symbol | resolved, see below |
| any other object | itself |
Resolving a symbol
- A namespace-qualified symbol is the value of the global var it names; it is an error if there is none, or if the var is private and in another namespace.
- A package-qualified symbol (
java.util.Date) is that Java class. - A symbol qualified by a class is a static field of the class; since Clojure 1.12 also a function that calls a static method (
Classname/method), an instance method (Classname/.method) or a constructor (Classname/new); and a digit 1 to 9 gives an array class (String/2). - An unqualified symbol is, in this order: a special form; a local binding (a parameter or a
letname); a class that the namespace maps; a var that the namespace maps; otherwise an error. - Metadata on a symbol is used by the compiler (type hints) and is not part of the value.
Calls
- A special form is a primitive the compiler understands: its evaluation rules are its own.
- If the operator is a symbol naming a macro, the macro function receives the unevaluated operand forms and the form it returns is evaluated in its place.
- Otherwise it is a function call: the operator and the operands are evaluated from left to right, the operator's value is used as an
IFnandinvokeis called with the arguments. Besides functions, keywords, symbols, maps, vectors and sets are callable:(:a m),(m :a),(v 0),(s x).
Loading and eval
load (a classpath resource), load-file, load-reader and load-string read and evaluate the forms of a source in order, and return the value of the last one. They run in a temporary context where *ns* has a fresh binding, so an in-ns inside the loaded code does not outlast the load. (eval form) evaluates a form as data, not text: (eval (list + 1 2 3)) is 6.
The special forms
These are the symbols that the compiler treats specially (the table of specials in Compiler.java, and the reference page on special forms). Several are written with a macro in front: let, loop, fn, letfn, case and import expand to let*, loop*, fn*, letfn*, case* and import*.
| Form | Meaning |
|---|---|
(def symbol doc-string? init?) | creates or finds a global var in the current namespace; with init sets its root binding. Yields the var itself. Metadata on the symbol becomes metadata of the var; the keys :private, :doc, :test and :tag are special, and the compiler adds :file :line :name :ns :macro :arglists |
(if test then else?) | only nil and false are false; a missing else is nil |
(do expr*) | evaluates the expressions in order and returns the last (nil for none) |
(let [binding*] expr*) | sequential lexical bindings, each a binding form (a symbol or a destructuring form) and an expression; the body is an implicit do. Locals are not variables: their values never change |
(quote form) | the unevaluated form; 'form |
(var symbol) | the var object, not its value; #'x |
(fn name? [params*] expr*) or (fn name? ([params*] expr*)+) | a function, with one or several arities, optionally named so that it can call itself; see Functions |
(loop [binding*] expr*) | like let, and a recursion point |
(recur expr*) | jumps to the nearest recursion point (a fn arity or a loop) with new values, evaluated first and bound in parallel. Must be in tail position, must match the arity exactly (a variadic rest parameter takes a single seq) and does not use the stack. There is no tail-call optimisation otherwise |
(throw expr) | throws the value, which must be a Throwable |
(try expr* (catch classname name expr*)* (finally expr*)?) | the first catch whose class matches the exception is used, with name bound to it; finally runs for its effects |
(monitor-enter expr), (monitor-exit expr) | synchronisation primitives; use locking |
(. instance-expr member args*), (new Class args*), (set! (. target field) expr) | interop, see Java interop |
(set! var-symbol expr) | sets the thread-local binding of a dynamic var |
(letfn* …), (case* …), (import* …), (deftype* …), (reify* …) | the primitives under the macros letfn, case, import, deftype and reify |
& | marks a rest parameter; it is not a value |
catch, finally | words that only mean something inside try |
Conditionals and iteration (macros)
| Form | Meaning |
|---|---|
when when-not if-not | (when test body*) is (if test (do body*)); when-not and if-not negate the test |
if-let when-let if-some when-some when-first | bind a name to the test value and use it if the test is true (if-some and when-some test for non-nil instead of truth) |
cond | (cond test expr test expr … :else expr): the first true test wins; :else is just a true keyword |
condp | (condp pred expr test-expr result …): the first clause for which (pred test-expr expr) is true; a clause written test-expr :>> result-fn calls the function with the result of the predicate (:>> is an ordinary keyword); a final lone expression is the default, and without one an IllegalArgumentException is thrown |
case | (case expr test-constant result … default): constants are literals and are not evaluated; a list of constants (a b c) matches any of them; it dispatches in constant time and throws IllegalArgumentException if nothing matches and there is no default; a vector can be used to match a list, since lists group constants |
and or | short-circuit; return the deciding value, not a boolean |
dotimes doseq for | (dotimes [i n] …) runs for effect; doseq iterates for effect; for builds a lazy sequence. doseq and for take several bindings and the modifiers :let [..], :when test and :while test (for and doseq nest the later bindings inside the earlier) |
while | repeats a body while the test is true |
doto | (doto x (f a) (g b)) calls each form with x inserted after the operator and returns x |
assert | throws AssertionError if the expression is false and *assert* is true |
time, comment, delay, lazy-seq, locking | time an expression and print the time; ignore forms and return nil; delay a computation; delay a sequence; hold a monitor |
Threading macros
A threading macro passes a value through a pipeline. The word thread here has nothing to do with concurrent threads.
| Macro | Puts the value |
|---|---|
-> | as the first argument of each form |
->> | as the last argument of each form |
as-> | wherever the given name appears: (as-> x v (f 1 v) (g v 2)) |
some-> some->> | like -> and ->>, and stop with nil when a step returns nil |
cond-> cond->> | apply each step only when its test is true |
(defn transform [person]
(-> person
(assoc :hair-color :gray)
(update :age inc)))
Transients
(transient coll) gives a mutable-looking copy of a vector, hash map or hash set in constant time, which shares structure with its source and cannot change it. The operations conj! assoc! dissoc! pop! disj! work on it, and you must use their return value (they are not meant to be changed in place). persistent! makes it persistent again in constant time, after which the transient must not be used. Transients support the read-only interface (get, nth, count, calling) but not the persistent one (conj, assoc throw). Lists have none.
Not described
Known, not yet covered
- Reference: type hints and primitive type inference (
*warn-on-reflection*, primitive function signatures such as^longon parameters), the compiler options (*compiler-options*, direct linking) and*unchecked-math*are named but not described; the reference page on compilation was read only for what is on File structure. - Reference: the full list of macros of
clojure.coreis on the standard library page; only the common ones are explained here.
Unknown
- Reference: the descriptions of
condpandcasecome from their documentation strings incore.clj; the descriptions ofwhen-first,while,doto,assertandtimecome from knowledge of the language and were not checked against their documentation strings.
See also
- Macros and syntax-quote – how forms are rewritten before evaluation
- Functions and destructuring –
fn, arities, destructuring and conditions - Common Lisp: special operators – the same idea in Common Lisp