◄ WORLD V · SONNY 5DART 149 · a helldive at the net

CONTINUATION-PASSING nothing returns; it calls its continuation

Give every function one extra argument — a continuation representing “the rest of the computation” — and nothing ever returns: each operation calls its continuation with its result. Control flow (returns, calls, jumps) stops being hidden machinery and becomes data you pass around. It is the compiler intermediate form (Steele’s RABBIT) and the way to make call/cc, exceptions, and generators explicit.

THE TECHNIQUE pass the rest of the computation

Evaluate (2+3)*4 in CPS. add(2,3,k) computes 5 and calls its continuation k; that continuation is “multiply by 4, then halt.” So: add(2,3, r→mul(r,4, r2→halt(r2))) — control is explicit, and nothing uses a native return. Below: the continuation as an explicit stack of pending operations. live demo


  

HISTORY & CREDIT many discoverers; the transform is Fischer

“Continuations have one inventor” — no; Reynolds famously catalogued their many independent discoveries. cited

1964 · van Wijngaarden gives the earliest known CPS transform (as a preprocessing step); Landin’s SECD dump already is a reified continuation.
1972 · Michael Fischer — the CPS transform proper (“Lambda calculus schemata”), relating direct and continuation styles.
1975 · Plotkin — proves the call-by-value / CPS simulation theorem (CPS makes evaluation order explicit and strategy-independent).
1976 / 1978 · Guy Steele — “Lambda: The Ultimate Declarative” (1976, the concept), then the RABBIT Scheme compiler (1978 thesis) where CPS is the intermediate form, so tail calls become jumps — the lineage behind SSA and modern IRs.

The “discovery” is genuinely plural — Reynolds’ 1993 history names van Wijngaarden, Mazurkiewicz, Morris, Fischer, and the SECD dump. Fischer 1972 / Steele 1976

RECOMMEND FOR I-13 a continuation = a defunctionalized stack

I-13 has no closures, so the continuation is defunctionalized into an explicit stack of pending operations — which is exactly the SECD dump:

$ i13 run cps.i13 # (2+3)*4 with the continuation as a pending-op stack add(2,3) -> 5, invoke continuation [MUL 4] -> 5*4 = 20, invoke [HALT] -> 20 result = 20 (control explicit; nothing used a native return)
Recommend: nothing new — a continuation is a stack of pending [op, arg] frames in an f64 array; each operation computes its value and invokes the continuation (a recursion that pops the next frame), so “the rest of the computation” is data (verified (2+3)*4 = 20). Because I-13 has no closures, the continuation is defunctionalized (dart 147) into that stack — which is precisely the SECD dump (dart 142) and the CEK machine’s K.
Note: this closes the batch’s loop — SECD’s dump, Krivine’s stack, and CPS’s continuation are the same object (a reified continuation), all runnable on a closureless language by defunctionalization. First-class call/cc wants to capture the whole stack as a value (a saved-state arena, PS-015).