Implementing Interactive Languages
It&#x27;s an interesting article. Has some oversights but still a good design space to think about.<p>I like the Futamura projections approach. The associated description of write a partial evaluator and you get a compiler for free is starting to look like an inside joke - compilers are easier to write than partial evaluators - but the Graal&#x2F;RPython approach where someone else has already written the partial evaluator and you strap on your own interpreter has definite merit.<p>There&#x27;s also this community wisdom, echoed in the article, that python can&#x27;t be made fast because the runtime extensible language semantics get in the way. Somehow that is known to be true despite javascript having the same properties <i>and</i> very fast implementations, and people are still holding onto it as truth after Mojo starts talking about statically compiling the static parts of the program. Also the self and lisp implementations that came before.<p>--------<p>For what it&#x27;s worth, my current thinking on this goes:<p>0&#x2F; Representation. Define a static single assignment form bytecode<p>1&#x2F; Front end. Translate source semantics into that bytecode as simply as possible<p>2&#x2F; Runtime. Write a machine code implementation of each opcode used by 1&#x2F;<p>3&#x2F; Back end. Compile the bytecode to a series of (machine code) calls to said opcodes<p>That&#x27;s the baseline compiler. No baseline interpreter involved. Main design goal of each step is to do the absolute bare minimum work possible. An argument could be made that the above describes a template jit if you do the translation at runtime, or a naive ahead of time compiler if you do it before running the program. They&#x27;re the same thing really.<p>Going from there to non-baseline compiler involves increasingly heroic optimisations on the SSA form and adding operations to the bytecode that correspond to things like memory operations on the target machine.<p>--------<p>The compiler runtime is worth a mention as it&#x27;s something which doesn&#x27;t get much love in compiler implementation discussion. I didn&#x27;t realise it existed before  I started working on these things. A compiler is a translator from language A to B. Wherever something in A doesn&#x27;t have an obvious counterpart in B - maybe A has hash tables and B is x86 machine code - either the compiler emits code that makes the thing happen anyway, or it emits a call to a pre-written function.<p>This might be integer division written as a loop for hardware that doesn&#x27;t have integer divide, or it might be an openmp offloading construct parametrised on a function pointer. You can essentially move complexity out of the compiler and into the supporting library, and functionality can move between the two at will. If the runtime library is written in (or compiled to) the compiler intermediate representation instead of machine code, you can get the same performance and semantics as having the compiler build the same code on the fly.