The Tower API
lib/tower.naj evaluates to a selector function over five exports.
The session uses four of them at boot; all five are available to any
floor program that animates the file:
(let tower (evalms nil (trans (car (read-file 'lib/tower.naj)) nil))
(let m ((tower 'make-level) 0)
(let env ((tower 'make-env) 0)
(((((tower 'ev) m) env) '(+ 1 2))))))
;=> 3
Exports
((T 'make-level) 0)— construct a meta level: a pair(menv . slot)whose environment holds a fresh set of handlers and whose slot lazily yields the level above.((T 'make-env) 0)— a fresh object-level global environment: one mutable frame holding the primitives.(((T 'env-define) env) name)then applied to a value — extend a global environment’s first frame with a cell-backed binding. This is the hooklib/purple.najuses to install the session bindings.((((T 'ev) m) env) exp)— evaluateexpinenv, dispatched through meta levelm, at the concrete stage.(((T 'clambda-code) m) e)applied tor— the reified residual of aclambdaexpressionein environmentr: code for a stage-polymorphic function, before anyrun. Theclambdahandler is this plusrun 0; the export exists so the staging of the tower can be inspected without executing it.
Value representations
| shape | meaning |
|---|---|
| number, symbol, nil, pair | themselves |
('clo params body env . m) | interpreted closure; m is the meta level at creation |
('prim . name) | primitive |
('fn . fc) | native function — a floor closure under the handler protocol |
('cont . k) | captured continuation (k a bare floor closure) |
Floor values outside these shapes (Pink evaluators, nil-env, floor
closures generally) apply natively to one forced argument, so curried
floor code runs through the tower unchanged. Numbers, symbols, and
nil in function position throw ('cannot-apply . v).
Environments
An environment is a list of frames. A runtime frame is a cell over an
association list — define extends it in place; a plain list in
frame position is a staging-time frame (introduced during clambda
compilation, matching lms-black’s inRep environments). Bindings are
(name . ('cell . c)) when mutable; the primitives are bound direct,
cell-less, so staged reads of them constant-fold.
The meta frame
A level’s environment holds fifteen handlers, each an ('fn . fc)
boxed in a cell so EM can set! it:
base-eval dispatch on expression shape
eval-var variable lookup
eval-lambda both lambda shapes (lms-black and floor/pink)
eval-clambda compile-at-definition (see the clambda chapter)
eval-let both let shapes
eval-cst cst: let over direct (cell-less) bindings; staged reads fold
eval-if three-armed if
eval-begin sequencing
eval-set! assignment (throws set-immutable on cell-less bindings)
eval-define first-frame extension
eval-quote quotation
eval-EM evaluate one level up (materializes it if needed)
eval-application evaluate operator and operands, then base-apply
eval-list evaluate a list of expressions
base-apply apply an object value
followed by the eleven primitives, direct: + - * eq? cons
car cdr number? symbol? null? pair?.
A handler installed with EM (set! …) receives three arguments —
the expression, the environment, and the continuation — and usually
ends by deferring to the saved original, as in the EM chapter.
The stage dictionary
Every handler threads l, a selector over nine operations:
staged (0 or 1), lift, force, cread/fread (cell read,
dread-preserving and forced), cset, cnew, app (dynamic apply),
appk (apply a continuation). The concrete dictionary — identity
lift and force, real cell operations, real application — makes ev
an interpreter. make-staged-dict builds the compiling dictionary
used under clambda: pure values lift, cell operations and dynamic
applications emit residual code routed through the dictionary the
compiled function will receive at run time. Handler dispatch itself
is always concrete; only dictionary operations mark where staging
residualizes.