back

by iLemming·2d ago·view on hn ↗
That is badly generalized proverb. Clojure has unhygienic macros, no expander extension points, and no error-message layer, so "don't" is a rational default there. Yet, for example Racket writes macros that write macros as a matter of course. And that's because the language has the infrastructure: hygiene by default, phase separation, `syntax-parse` with grammar-quality error messages, a module system that actually knows about compile-time dependencies.

But even with some limitations, there are cases in Clojure where macros do achieve things otherwise unattainable. One practical example is multi-host code generation. You can have a Clojurescript macro that parses and analyzes some javascript lib, doing that on JVM side, while emmiting code generated to run in Javascript. Hyperfiddle/Electric is the best example of that kind of macro ingenuity.

Or take core.async's `go`. It takes an arbitrary body, analyzes it into an AST, and rewrites it into a state machine so that <! and >! can park and resume. On the JVM it lets you avoid blocking threads. In Clojurescript it is the only way to have the model at all, because JS has no threads to block.

Nothing about that is expressible as a function. The transformation needs the entire body as data. Same family: core.match compiles a pattern matrix into a decision tree. And there are many more example use cases for macros.

"Don't write macros" rule has the second part: "unless you truly have no choice."

1 comments
My own personal rule is that every Lisp programmer should understand macros and very rarely reach for them. They are a super-power, but easy to misuse. I think Paul Graham is partly responsible for the “Lisp = macros” thinking and the general overuse of them. On Lisp was a great book but it does lots of things I wouldn’t consider good practice today. After Graham popularized Lisp and macros, there was a period when every Lisp newbie, myself included, was writing macros all the time. Now that I’m older, I know better. Clojure is my daily driver and I haven’t written a macro in years (though I’ve debugged ones other people have written). But I know defmacro is there and I can use it anytime I really need to.
I don't think Paul ever envisioned Lisp evolving into something like Clojure, where macros indeed are a bit less than a first-class citizen. And I think the whole adage of "First rule of macro club - don't write macros. Second rule of macro club - don't dispute the first rule...", etc. became a thing only with Clojure. I don't disagree with you - genuine use cases for macros are uncommon. Yet it's an instrument - nothing's wrong with using it properly. Knowing when and how is a skill, experienced Lispers indeed - rarely reach for it.