# §3 Special Forms **Status**: catalog complete; normative exemplars for `if` and `let*`; the remaining entries follow the same format (tracked in `coverage.md`). A *special form* is a form whose head symbol is evaluated by rule rather than by function application or macroexpansion. The special forms of Clojure are: > `def` · `if` · `do` · `let*` · `fn*` · `loop*` · `recur` · `quote` · `var` > · `throw` · `try`/`catch`/`finally` · `set!` · `monitor-enter` · > `monitor-exit` (host) · the interop forms `.` and `new` (host) `let`, `fn`, `loop`, `and`, `or`, `when`, … are **macros** over these (§8); implementations MUST treat them as redefinable macros, not additional special forms. `monitor-enter`/`monitor-exit`, `.` and `new` are host forms: their syntax is specified here, their behavior is host-defined. Special-form head symbols are not shadowable: a binding named `if` does not change the meaning of `(if ...)` in operator position. ⚠ This matches the reference; it differs from Scheme. --- ### if — since 1.0 ``` (if test then) (if test then else) ``` **Semantics** - S1. `test` MUST be evaluated first, exactly once. - S2. Every value other than `nil` and `false` is *logically true*. If the value of `test` is logically true, `then` MUST be evaluated and its value returned; otherwise `else` (or `nil` when absent) MUST be evaluated and its value returned. - S3. The branch not taken MUST NOT be evaluated. - S4. `if` MUST be usable in tail position with respect to `recur` (§3 `recur`): an `if` whose branch is a `recur` form is a valid recur target path. **Edge cases** - E1. `(if test then)` with a logically false `test` evaluates to `nil`. - E2. The empty collections (`()`, `[]`, `{}`, `#{}`), the number `0`, and the empty string `""` are logically **true** (only `nil`/`false` are false). ⚠ This differs from several Lisps and is a frequent divergence source in alternative implementations. **Errors** - X1. `(if)` and `(if test)` with fewer than two argument forms, or more than three, MUST be a compile-time error. **Examples** ```clojure (if 0 :t :f) ;=> :t (if "" :t :f) ;=> :t (if nil :t :f) ;=> :f (if false :t) ;=> nil ``` **Conformance** S1–S3, E1–E2 → jolt `forms-spec` "if/do/def" group; truthiness group in `truthiness-spec`; clojure-test-suite `core_test/if.cljc`. S4 → `forms-spec` fn/loop recur cases. X1 → `forms-spec` "if arity (X1)" (0/1/4-arg forms throw in both the analyzer and the interpreter). --- ### let* — since 1.0 ``` (let* [sym₁ init₁ … symₙ initₙ] body…) ``` `let*` is the primitive sequential-binding form. The user-facing `let` macro adds destructuring and expands to `let*` (§8); `let*` itself accepts **only simple symbols** in binding positions. **Semantics** - S1. Each `initᵢ` MUST be evaluated in order, exactly once, in an environment where `sym₁…symᵢ₋₁` are bound to their values (sequential scope, as Scheme `let*`). - S2. The body forms MUST be evaluated in order with all bindings in scope; the value of the last body form is the value of the `let*` form. An empty body evaluates to `nil`. - S3. A later binding MAY rebind the same symbol; each binding creates a new lexical binding visible from the next init onward (no mutation of the earlier binding is implied). - S4. Bindings are lexical and immutable: there is no form that assigns to a `let*`-bound local. (Closures capture bindings by value; see §3 `fn*`.) - S5. The binding vector MUST be a vector literal with an even number of forms. **Edge cases** - E1. `(let* [] body)` is valid and equivalent to `(do body…)`. - E2. Binding a symbol that names a var shadows the var for the lexical extent of the body; `(var sym)` within that extent still denotes the var. **Errors** - X1. An odd number of binding forms MUST be a compile-time error. - X2. A non-symbol in a binding position (e.g. a destructuring pattern) MUST be a compile-time error for `let*` — destructuring belongs to the `let` macro. ("Bad binding form, expected symbol" in the reference.) **Examples** ```clojure (let* [a 1 b (+ a 1)] (* a b)) ;=> 2 (let* [x 1 x (inc x)] x) ;=> 2 (let* [] 42) ;=> 42 ``` **Conformance** S1–S3, E1 → jolt `forms-spec` let group; clojure-test-suite `core_test/let.cljc`; jank corpus `form/let/*`. X2 → jolt `destructuring-spec` "primitives reject patterns". S4, X1 → UNVERIFIED (cases to add). --- ## Remaining entries (format above; status in coverage.md) | Form | Notes for the entry author | |---|---| | `def` | var creation vs re-binding; metadata on the name; `(def x)` unbound; return value is the var | | `do` | empty `(do)` → nil; top-level `do` splices for compilation units (important and under-documented) | | `fn*` | arities, variadic `&`, closure capture, self-name, simple-symbol params only, recur target | | `loop*` | recur arity must match bindings; recur rebinds in place | | `recur` | tail-position rule (normative definition of tail position needed), across `if`/`do`/`let*`/`try` interactions | | `quote` | self-evaluation table: which literals are self-evaluating unquoted | | `var` | `#'` reader sugar; resolution at compile time | | `throw` | any value vs Throwable — host question; jolt/cljs allow data, reference requires Throwable → classification needed | | `try/catch/finally` | catch dispatch order, `:default`-style catch-all is a dialect extension (⚠ divergence note), finally evaluation guarantees, value of try | | `set!` | host-dependent (dynamic vars + host fields) | | `.` / `new` | syntax only; behavior host-defined |