Derived Forms

cond, case, and, or, when, unless, let*, named let, do, quasiquote, and guard are built into the normalizer rather than written as user macros.

See it run

LISPEX
(let* ((x 6) (y (+ x 1))) (* x y))

Observed result

OUTPUT
42

How to reason about it

  • Expansion uses fresh names and hidden built-in helpers, so shadowing a name in your own code cannot change what an expansion means.
  • Normalization preserves defined tail positions and source diagnostics.
  • guard is a fixed core-facing construct for catchable errors, not a general macro facility.

What each written form becomes

Written formCore strategyPinned boundary
cond / casenested if with fresh temporaries, and case uses a hidden eqv?tail clause preserved, and no cond =>
and / orshort-circuit nested ifsingle final operand remains tail
when / unlessif plus begin or zero-value valuesfalse path returns zero values
named let / dohygienic letrec looprecursive application is tail
quasiquotequote plus hidden constructorsbare unquote outside quasiquote is static E1xx

A common mistake

define-syntax, syntax objects, and arbitrary expansion are rejected.

Keep going

Core Forms holds the shapes these rewrite into, and the manual helps you choose between the conditionals.

Core Forms · Conditionals and Derived Forms