Macros and Metaprogramming

Metaprogramming is the practice of writing programs whose data and output are themselves programs. A macro is one common form: a construct that runs during compilation or expansion and transforms one piece of code into another before the final program is run. This lets a programmer add new syntactic constructs to a language rather than only new functions.

The Lisp family takes this furthest because of homoiconicity: code is represented in the same nested-list data structures that the language manipulates as data. Clojure’s reference documentation states that it “has a programmatic macro system which allows the compiler to be extended by user code,” and notes that many of the language’s own constructs, including branching and looping forms, are implemented as macros rather than as built-in primitives. A macro receives the unevaluated forms passed to it and returns new forms to be compiled in their place.

This idea rests on an old observation. The Structure and Interpretation of Computer Programs notes that in Lisp, “descriptions of processes, called procedures, can themselves be represented and manipulated as Lisp data.” Because programs and the data they operate on share one representation, a program can treat another program as ordinary input to inspect and rewrite, which is exactly what a macro does.

Languages outside the Lisp tradition reach similar goals through different mechanisms, such as C++ templates, reflection, or external code generation. These approaches can produce or specialize code, but they generally do not give the uniform code-as-data manipulation that homoiconic macro systems provide.