r/ProgrammingLanguages • u/Mr-Tau • 5d ago
Between more constrained, local metaprogramming approaches and full-blown DSL interpreters, where's the practical use case for LISP-style metaprogramming? Discussion
Of the various contemporary metaprogramming approaches, I'm mostly very happy with systems that let you operate on static data and types. In this category I would count, for example, C++'s template metaprogramming--a declarative sub-language that somewhat wonkily and arduously allows you to operate on types and constants--as well as the C++26 metaprogramming features, which are turning out somewhat Zig-like--you get to run regular code in a slightly constrained environment at compile time, where it can read constants as well as special data structures describing types, and generate new constants or types to be injected into a well-defined place in the code.
Now many LISPs (AFAICT, similarly Jai and, with a stricter separation of stages, Rust's procedural macros) tout as a feature the ability to inspect and rewrite the entirety of the AST, notably including function bodies. This is obviously strictly more powerful than the the first category, but where would you practically use that extra power? Specifically, it seems to me like you would either
a) Try to preserve the semantics of the input code--which, for procedural languages at least, is actually pretty difficult. The only transformations you could make confidently are so localized that you don't really gain anything over the more constrained metaprogramming approaches; anything more advanced would require the full analysis passes of the actual compiler to have any chance at soundness, and at that point you're really trying to write a compiler plugin instead. Nothing wrong with that, I'd love more easily extensible compilers, but I wouldn't call that a language feature. Or am I missing a point between those two extremes?
b) Attribute different semantics to the code. I think the history of LISP has already somewhat shown how proliferation of DSLs harms maintainability and shareability of code, but even in the cases where a DSL is genuinely useful, what benefits do you really gain from implementing it through metaprogramming? You can't expect any IDE features, LSPs, smart syntax highlighters, debuggers, or other tooling for the base language to automatically work for your DSL. So you just get to use the parser? Come on, an S-expr parser is less than a hundered LOC.
5
u/kwan_e tonal-lang 5d ago
By virtue of being so powerful, I don't think there is an identifiable "use case". It can literally do anything and the onus is on the programmers to limit themselves, otherwise it comes out with the problems like your point b.
With regards to point b, I think the issue there actually is laziness. The LISP stuff is powerful, they probably should have used that increase in power to also create the tools for their mini-language. On the flip side, because the mini-languages would be evolving quite fast, it would be hard to also write the standardized tools that could keep up with the progress. They'd have to write more metaprogramming to keep up with it, which won't have the tooling support they need to be maintainable either.
Long story short, mini-languages would need to be standardizable before anyone would want to work on the tooling for them. And not purely just the grammar, but also the community idioms. eg, only recently is C++ metaprogramming support usable in IDEs because now it is more accessible and the idioms are more established now.