Hacker News new | ask | show | jobs
by ajross 506 days ago
Maybe it's time to just recognize that lisp-style macros-as-language-syntax features just aren't worth the struggle and grief?

The big metaprogramming feature traditionally implemented in macros, type generation, is already provided in some form by all major languages already.

And an awful lot (and I mean an awful lot) of good work can be done at the string replacement level with cpp. And generating code upstream of the compiler entirely via e.g. python scripts or templating engines is a very reasonable alternative too. And at lower levels generating code programmatically via LLVM and GPU shaders is well-trodden and mature.

Basically, do "macros" really have a home as a first class language feature anymore?

3 comments

Oh how I enjoy trying to compile and use projects where they use some complex home brew codegen system often written in a different language entirely [1]. Luckily they often use Python as part of some core build step which never breaks compatability in their regex librwry [2]. </sarcasm>

Yes macros can be a pain and should be limited, but in my experience, a couple hundred lines of macros replaces many thousands of lines code generators with complicated baroque build system integrations (ahem ROS2). The tradeoff is even worse when the language supports templates and compile time operations which can usually replace macros with even less code and are easier to understand. Though at least Go supports codegen properly with support in its official tooling.

1: https://github.com/google/flatbuffers/blob/master/src/idl_ge... 2: https://github.com/python/cpython/issues/94675

Bad code is bad code, but FWIW every time I've seen something that actually needs a "complex home brew codegen system", it's absolutely best expressed in a dedicated piece of software engineering and not within a macro environment. Zephyr leans heavily on that kind of engineering, actually.

The point is really that "macros" is a weird sandwich between "complicated metaprogramming you need to do from first principles" and "you really didn't need metaprogramming, did you?". And that over the years that sandwich has been getting thinner.

Lisp macros in the 60's were a revelation. They don't really have a home anymore.

> FWIW every time I've seen something that actually needs a "complex home brew codegen system", it's absolutely best expressed in a dedicated piece of software engineering and not within a macro environment. Zephyr leans heavily on that kind of engineering, actually.

I'd definitely agree with you about Zephyr's "I really want C macros to be real macros" system for it's device tree system. It's a huge pain to debug because you don't find issues until the linking step. However I'd counter with the fact that C macros _aren't_ real macros. Hence the pain.

Actually C's "macros" are literally just a separate codegen tool (m4 processor) just like what you're suggesting. It's a large part of what makes C code so hard to programmatically parse or automate.

Essentially Zephyr is doing meta-programming in an external codegen tool. Totally agree that a separate codegen tool specifically for device trees or whatnot could be better than that. However if C had a proper macro system (and compile time types), you could readily express the device tree system in the language at compile time and produce helpful errors.

> The point is really that "macros" is a weird sandwich between "complicated metaprogramming you need to do from first principles" and "you really didn't need metaprogramming, did you?". And that over the years that sandwich has been getting thinner.

They aren't really though. Homebrew codegen systems are much more of the weird sandwich between "you needed metaprogramming" and "you didn't have metaprogramming". Instead you build a system which is even more complicated in total, e.g. source and generated code gets out of sync, you have name clashes, the core API logic is spread out over different languages and code bases, etc.

Though I do agree the use cases for macros are shrinking, but more due to meta-programming via templates and compile time expressions becoming more powerful and usually preferable method to doing meta-programming.

They seem to work well in Rust.
In another world, macros could have filled the role that programmable yaml fills today.
|-

  help  

  me