On page 196 of "Control Operators, the SECD Machine, and the \lambda-Calculus", and on page 4 of the technical report with the same name,
Matthias Felleisen and
Daniel P. Friedman wrote "The [CEK] machine is derived from Reynolds' extended interpreter IV.", referring to
John Reynolds's Interpreter III in "Definitional Interpreters for Higher-Order Programming Languages". To wit, here is an implementation of the CEK machine in
OCaml, representing lambda terms with de Bruijn indices: type term = IND of int (* de Bruijn index *) | ABS of term | APP of term * term Values are
closures, as
invented by
Peter Landin: type value = CLO of term * value list type cont = C2 of term * value list * cont | C1 of value * cont | C0 let rec continue (c : cont) (v : value) : value = match c, v with C2 (t1, e, k), v0 -> eval t1 e (C1 (v0, k)) | C1 (v0, k), v1 -> apply v0 v1 k | C0, v -> v and eval (t : term) (e : value list) (k : cont) : value = match t with IND n -> continue k (List.nth e n) | ABS t' -> continue k (CLO (t', e)) | APP (t0, t1) -> eval t0 e (C2 (t1, e, k)) and apply (v0 : value) (v1 : value) (k : cont) = let (CLO (t, e)) = v0 in eval t (v1 :: e) k let main (t : term) : value = eval t [] C0 This implementation is in
defunctionalized form, with cont and continue as the first-order representation of a continuation. Here is its refunctionalized counterpart: let rec eval (t : term) (e : value list) (k : value -> 'a) : 'a = match t with IND n -> k (List.nth e n) | ABS t' -> k (CLO (t', e)) | APP (t0, t1) -> eval t0 e (fun v0 -> eval t1 e (fun v1 -> apply v0 v1 k)) and apply (v0 : value) (v1 : value) (k : value -> 'a) : 'a = let (CLO (t, e)) = v0 in eval t (v1 :: e) k let main (t : term) : value = eval t [] (fun v -> v) This implementation is in left-to-right
continuation-passing style, where the domain of answers is polymorphic, i.e., is implemented with a
type variable. This continuation-passing implementation is mapped back to direct style as follows: let rec eval (t : term) (e : value list) : value = match t with IND n -> List.nth e n | ABS t' -> CLO (t', e) | APP (t0, t1) -> let v0 = eval t0 e and v1 = eval t1 e in apply v0 v1 and apply (v0 : value) (v1 : value) : value = let (CLO (t, e)) = v0 in eval t (v1 :: e) let main (t : term) : value = eval t [] This direct-style implementation is also in defunctionalized form, or more precisely in closure-converted form. Here is the result of closure-unconverting it: type value = FUN of (value -> value) let rec eval (t : term) (e : value list) : value = match t with IND n -> List.nth e n | ABS t' -> FUN (fun v -> eval t' (v :: e)) | APP (t0, t1) -> let v0 = eval t0 e and v1 = eval t1 e in apply v0 v1 and apply (v0 : value) (v1 : value) : value = let (FUN f) = v0 in f v1 let main (t : term) : value = eval t [] The resulting implementation is
compositional. It is the usual Scott-Tarski
definitional self-interpreter where the domain of values is reflexive (
Scott) and where syntactic functions are defined as semantic functions and syntactic applications are defined as semantic applications (
Tarski). This derivation mimics
Danvy's rational deconstruction of
Landin's
SECD machine. The converse derivation (closure conversion, CPS transformation, and defunctionalization) is documented in John Reynolds's article "Definitional Interpreters for Higher-Order Programming Languages", which is the origin of the CEK machine and was subsequently identified as a blueprint for transforming compositional evaluators into abstract machines as well as vice versa. == Modern times ==