← back to programming

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.

eval apply procedure + args body in new env environment The eval/apply cycle — mutual recursion is the whole interpreter.

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.

Scheme

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.

Scheme

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.

Scheme

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.

Scheme

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.

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): bodySyntactic 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.

Ready for the real thing? Read SICP §4.1.