I’ve noticed a small but interesting trend in the programming languages space. I’m not sure how novel it is, but this pattern, which I’ll refer to as “biphasic programming,” is characterized by languages and frameworks that enable identical syntax to express computations executed in two distinct phases or environments while maintaining consistent behavior (i.e., semantics) across phases. These phases typically differ temporally (when they run), spatially (where they run), or both.1 One can say that metaprogramming systems are related to biphasic programming. For example, C pre-processing can be thought of as biphasic programming in spirit since it allows you to run code in the pre-processor, a phase of compilation before runtime. But it doesn’t satisfy the definition I’ve provided since the preprocessor only does textual substitutions, and C’s preprocessor macros are limited – #ifdef is quite different from a bona fide if statement. Lisp-style hygenic macros like those in Scheme and Racket, on the other hand, are expressed through functions that support the same expressiveness as the base language(s), so I think it would be fair to say Lisps provide some of the oldest examples of biphasic programming. ↩
This pattern [(multi-stage programming)], which I’ll refer to as “biphasic programming,” is characterized by languages and frameworks that enable identical syntax to express computations executed in two distinct phases or environments while maintaining consistent behavior (i.e., semantics) across phases. These phases typically differ temporally (when they run), spatially (where they run), or both.
The scala 3 page on multi staged programming: https://docs.scala-lang.org/scala3/reference/metaprogramming/staging.html