Metacircular Evaluator
Abelson & Sussman · 1996 · SICP §4.1
A Scheme interpreter written in Scheme. eval dispatches on expression type, apply dispatches on procedure type. The mutual recursion between eval and apply is the whole story.
The core of the evaluator — eval
eval takes an expression and an environment. It classifies the expression — self-evaluating? variable? special form? application? — and dispatches accordingly. For applications, it evaluates the operator and operands, then calls apply.
The core of the evaluator — apply
apply takes a procedure and a list of arguments. For primitive procedures, it calls the underlying implementation. For compound procedures, it extends the environment with bindings for the formal parameters and evaluates the body.
Representing expressions
The evaluator classifies expressions with simple predicates: self-evaluating?, variable?, quoted?, assignment?, definition?, if?, lambda?, begin?, and application?. Each predicate checks the tag (first element) of the expression list. This is data-directed programming applied to syntax.
Evaluator data structures — environments
An environment is a chain of frames. Each frame is a table of bindings (name → value). Variable lookup walks the chain from innermost to outermost. define adds to the first frame; set! mutates the first frame where the variable is found.
Running the evaluator
The driver loop reads an expression, evaluates it, and prints the result. This is the read-eval-print loop (REPL). The metacircular evaluator is a REPL implemented in the language it evaluates — Scheme interpreting Scheme.
Notation reference
| Scheme | Python | Meaning |
|---|---|---|
| (eval exp env) | eval(exp, env) | Evaluate expression in environment |
| (apply proc args) | proc(*args) | Apply procedure to arguments |
| (tagged-list? e 'if) | e[0] == 'if' | Check expression type by tag |
| (extend-environment ...) | dict(env, **new) | Create new frame over base env |
| (define (f x) body) | def f(x): body | Syntactic sugar for lambda + define |
Neighbors
Other reading pages
Foundations (Wikipedia)
Translation notes
The metacircular evaluator uses Scheme to interpret Scheme, so the host language and the interpreted language share the same syntax. In Python, we represent Scheme expressions as nested lists. This survives the translation: eval classifies expressions, apply classifies procedures, and they call each other. The only structural difference is that Python's compound procedures use dictionaries for environments while Scheme uses association lists.