Meta-programming = the broad idea of “programs that manipulate or generate programs”. It can happen at runtime (reflection) or compile-time (macros).
Macros = one specific style of meta-programming, usually tied to transforming syntax at compile time (in a pre-processor or AST-transformer). It takes a piece of code as input and replaces it with another piece of code as output, often based on patterns or parameters.
- Rule‑based transformation: A macro is specified as a pattern (e.g., a template, an AST pattern, or token pattern) plus a replacement that is generated when that pattern is matched.
- Expansion, not function call: Macro use is not a runtime call; the macro is expanded before execution, so the final code is the result of replacing the macro invocation with its generated code.
Here are some programming languages and their meta-programming and macro capabilities.
NB! Take with a grain of salt. The result comes from working with perplexity.ai, and I have not had a chance to personally verify all of the cells. They do look generally correct to me overall, though. Corrections are welcome!
Metaprogramming + macro features
Here are the programming languages with their scores (out of 15) and links to their repos or homepages:
- Racket: 15
- Common Lisp (CL): 13
- Scheme (R7RS‑small): 12
- Rust: 11
- Nim: 10
- Carp: 9
- Clojure: 10
- Jai: 5
- C++: 5
- Zig: 4
- Ruby: 4
Scores are out of 15 = 4 (metaprogramming) + 3 (compile‑time facilities) + 8 (macro features).
Each cell is either ✅ (yes) or – (no / limited).
| Feature / language | Racket | Common Lisp | Scheme (R7RS‑small) | Rust | Nim | Carp | Clojure | Jai | C++ | Zig comptime
|
Ruby |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Metaprogramming features: | |||||||||||
Runtime metaprogramming (e.g., open classes, define_method, method hooks) |
✅ | ✅ | – | – | – | – | ✅ | – | – | – | ✅ |
| Runtime reflection / introspection | ✅ | ✅ | ✅ | – | – | – | ✅ | – | ✅ | – | ✅ |
Runtime eval / dynamic code loading |
✅ | ✅ | ✅ | – | – | – | ✅ | – | – | – | ✅ |
| Build‑ or tooling‑level code generation supported | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Metaprogramming score (out of 4): | 4 | 4 | 3 | 1 | 1 | 1 | 4 | 1 | 2 | 1 | 4 |
| Compile‑time facilities (not strictly macros): | Racket | Common Lisp | Scheme (R7RS‑small) | Rust | Nim | Carp | Clojure | Jai | C++ | Zig comptime
|
Ruby |
| Run arbitrary code at compile time | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | – | ✅ | ✅ (constexpr) |
✅ | – |
| Types as values at compile time | ✅ (– but in Typed Racket) | – | – | ✅ | ✅ | – | – | ✅ | ✅ (constexpr + templates) |
✅ | – |
| constexpr‑style type‑level / compile‑time computation | ✅ | – | – | ✅ (const‑eval) |
✅ | ✅ | – | ✅ | ✅ (via constexpr) |
✅ | – |
| Macro features: | Racket | Common Lisp | Scheme (R7RS‑small) | Rust | Nim | Carp | Clojure | Jai | C++ | Zig comptime
|
Ruby |
| Hygienic identifier binding | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (gensym but manual) | ✅ | ✅ | – | – | – |
| Operate on AST / syntax tree | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | – | – | – | – |
| Pattern‑based transformations | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | – | – | – | – |
| Define new syntactic forms | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | – | – | – | – |
| Define new keywords / syntax | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | – | – | – | – |
| Override core language forms | ✅ | ✅ | ✅ | – | – | – | – | – | – | – | – |
| Multi‑phase / macros of macros | ✅ | ✅ | ✅ | ✅ | – | – | – | – | – | – | – |
| Full‑fledged DSL / language building (via macros) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | – | – | – | – |
| Macro & compile time features score (out of 11) | 11 | 9 | 9 | 10 | 9 | 6 | 6 | 4 | 3 | 3 | 0 |
| Total score | 15 | 13 | 12 | 11 | 10 | 9 | 10 | 5 | 5 | 4 | 4 |
| Racket | Common Lisp | Scheme (R7RS‑small) | Rust | Nim | Carp | Clojure | Jai | C++ | Zig comptime
|
Ruby |
The score counts one point per row where the language can reasonably do what the feature describes (DSL‑building is counted as a full feature, even if “limited” in some languages).
The feature score is not an ultimate measure of meta-programming power, since a language (like C++) may have a higher score than another language (like Ruby), but generally be considered less tailored for meta-programming than the other language (Ruby is generally revered for its powerful meta-programming abilities).
Macro features are varied and many, and thus in the total score they gain an undue weight, although runtime meta-programming may be just as, or even more, powerful.
Lisp-style languages (with their homoiconic S-expressions) make out 5 of the 11 languages in our list: Racket, CL, Scheme, Clojure, Carp.
Top comments (0)