See it run
(let* ((x 6) (y (+ x 1))) (* x y))Observed result
42let* binds x first, then uses it to compute y. Lispex rewrites these bindings into nested let forms before execution.
(let ((x 6))
(let ((y (+ x 1)))
(* x y)))Observed result
42Run 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 form | Conversion to core forms | Evaluation rule |
|---|---|---|
cond / case | nested 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 / or | nested if forms that stop when the result is known | the final operand keeps its tail position |
when / unless | if with begin for the body, or values with no arguments | returns zero values when the body is skipped |
named let / do | a letrec loop with names protected from collisions | the recursive call is in tail position |
quasiquote | quote and hidden data constructors | bare 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.