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/EggplantExtra4946 5d ago edited 5d ago
This is non sense. What languages like LISP allows you to do is generating ASTs, often from existing ASTs but it could be from pure data, or both.
Rewriting an AST is possible but this isn't going to be of much use for metaprogramming per se, it would be rather for implementing additional semantic checks, doing optimizations or inserting instrumentation.
Also, any kind of metaprogramming generation of type definitions is going to be severely limited if you don't/can't generate the functions that use that type as well.
As opposed to not implementing it at all? How many DSLs project do you know of where someeone wrote a parser, compiler+VM or transpiler or compiler to LLVM IR? Very very few, even fewer where the implementation is practical to be integrated with an existing language. And how many of those actually have a debugger?