Syntax That Expands into Basic Forms

See how Lispex rewrites shorthand into core forms while preserving evaluation order and the meaning of names.

See it run

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

Observed result

OUTPUT
42

let* binds x first, then uses it to compute y. Lispex rewrites these bindings into nested let forms before execution.

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

Observed result

OUTPUT
42

Run either example in the playground. Both return 42.

What normalization does

A form is a piece of program syntax. A derived form is shorthand that Lispex converts into core forms. This conversion is called normalization.

The conversion uses fresh internal names that cannot collide with names in your program. It also uses hidden built-in operations. Defining your own eqv?, for example, does not change how case compares data. These protections are called hygiene.

Normalization preserves evaluation order, tail positions, and diagnostics tied to the source. An expression is in tail position when its result is returned directly, with no further work in that call.

What each written form becomes

Written formConversion to core formsEvaluation rule
cond / casenested if forms with fresh temporary names; case uses a hidden eqv?preserves the last expression's tail position in each clause; cond => is rejected
and / ornested if forms that stop when the result is knownthe final operand keeps its tail position
when / unlessif with begin for the body, or values with no argumentsreturns zero values when the body is skipped
named let / doa letrec loop with names protected from collisionsthe recursive call is in tail position
quasiquotequote and hidden data constructorsbare unquote outside quasiquote is a static E1xx error

In case, a datum is one item of data, such as a number, symbol, or list. Clause data are compared using eqv? without being evaluated as expressions.

guard handles catchable errors through a dedicated core form. Its syntax and handling rules are built in too.

Built-in syntax rules

Lispex applies its built-in conversion rules before evaluation. It rejects define-syntax, syntax objects, and arbitrary user-defined expansion. Skipping a branch during execution does not bypass syntax checks on that branch.

Keep going

Look up the resulting syntax in Basic Syntax Forms. Choose between if, cond, and case in Writing Conditions.