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

THE ZINC MACHINE push the arguments, then the function — curried calls with no wasted closures

The ZINC machine (Leroy, 1990) fixed a real cost in the CAM: naive currying builds an intermediate closure for every argument, so f a b c allocates three times. ZINC's trick is to push all the arguments first and apply the function once — a multi-argument call costs one step, not one-per-argument, and closures are built only when a function is genuinely partially applied. This “push/enter with a mark on the stack” — the GRAB step, where the entered function itself matches its arity — is why OCaml's bytecode is fast. The ZINC experiment became Caml Light and then OCaml's bytecode runtime — the Z-in-C machine.

THE TECHNIQUE push args right-to-left, apply once; closures only on real partial application

A curried call add 2 3 then * 4. The demo pushes arguments and applies once, avoiding a per-argument closure: live demo


HISTORY & CREDIT Xavier Leroy, 1990

“Currying means one closure per argument.” — ZINC pushes every argument first and applies once, so a full call is a single step and closures appear only when you actually stop short. Curried syntax, uncurried cost. cited

1990 · Xavier Leroy — “The ZINC Experiment: an economical implementation of the ML language” (INRIA TR 117), push-args-then-enter.
lineage · derived from Krivine's machine; replaced the CAM (dart 269); became Caml Light, then OCaml's bytecode runtime.
now · the archetypal push/enter machine (Marlow & Peyton Jones, 2004, classify push/enter vs eval/apply; OCaml's native compiler uses eval/apply instead).

Push all the arguments, apply once, and build a closure only when a call really stops short. Curried on the surface, flat underneath — the reason ML bytecode is quick. Leroy 1990

RECOMMEND FOR I-13 curried application, computed

On the canonical compiler, add 2 3 = 5, then * 4 = 20 — a two-argument call applied at once:

$ i13 run m_zinc.i13 # add(2,3) then *4 RUN OK . 14 step(s) . peak stack 3 . call depth 1 curried = 5 result = 20
Recommend: ZINC's problem does not arise in i13, because i13 has no currying and no closures (its functions take a fixed argument list and capture nothing — dart 272). So there is no per-argument closure to eliminate: an i13 call pushes its arguments and applies once by construction, the very shape ZINC worked to recover. i13 reaches ZINC's efficiency for free by refusing partial application in the first place — the same “no closures” austerity that makes its stack analyzable. Where ZINC optimized currying, i13 declined it.