# Syntactic Macros (Rust & Lisp) March 2, 2025 I've been playing around with Rust macros a bit recently, and have been slightly annoyed when taking them in comparison with Lisp. To be clear, it including real macros rather than C-style textual macros (or no macros at all) is a real win for Rust, it's just annoying when taken in contrast with Lisp. Primarily, they're just too difficult to write due to Rust not having homoiconic syntax. Even the simplified `macro_rules!` ("Scheme-like") macros aren't the easiest to wrap one's mind around, and they're as limited as Scheme's hygienic macros system. And then the procedural ("Common-Lisp–like") macros, on top of being very complex to write, require being placed into a completely separate crate, and the stdlib provides zero batteries so one is basically required to pull in two or three other dependencies to write one. I do think that in particular `derive` macros are a stroke of genius, explicitly enabling and endorsing one of my two favorite genres of Lisp macros: automatically defining auxiliary types and helper methods for a type definition. Although in Rust it is a bit clunky because one can't design a partially bespoke syntax (IIRC the input to derive must be a *valid* struct, enum, or union), so one ends up with lots of `#[whatever]` annotations above every item; while in Lisp one can design a nice syntax suited to the particular macro, for example the macro for serializing/deserializing binary files from Practical Common Lisp[1]. Overall, it's just kinda disappointing to me that in Lisp macros are a tool in the toolbox; but in Rust due to the difficulty and complexity, any non-macro_rules macro is more a "tool of last resort", rather than something one doesn't have qualms about reaching for. I understand the tradeoffs and can see why they chose them[2], it just makes it disappointing as a Lisp aficionado. P.S. the second of my two favorite uses for Lisp macros is for computing things like lookup tables at compile-time without needing external code generation, which unfortunately is rare in Rust but is still sometimes seen, e.g. Rust-PHF[3]. And then the classic use of Lisp macros is introducing new control structures and such, where nontrivial instances just aren't possible in Rust as far as I can tell---or at least prohibitively difficult and feel non-native, since macros always look like function calls. The general preference is to write custom methods (possibly chained) that take closures when necessary; which for control-flow–like things is universally clunkier and harder to read IMO. [1]: https://gigamonkeys.com/book/practical-parsing-binary-files#designing-the-macros [2]: Yes I actually do understand the tradeoffs in great detail, thank you very much. The difficulty in writing macros is obviously just explainable because it doesn't use a very simple, homoiconic syntax. Then proc macros needing to be in a separate crate is because they need to be available at compile-time, and it's not feasible to try and pull them out from a mixed crate and compile them before compiling the rest of the crate, among a few other things. Multiple derive macros can be applied to one type, and it'd be confusing and inconsistent to allow them to completely transform the structure itself, because then there's multiple options for which version could be passed to the next macro, et cetera, et cetera. [3]: https://crates.io/crates/phf * * * Contact via email: alex [at] nytpu.com or through anywhere else I'm at: gopher://nytpu.com/0/about Copyright (c) 2025 nytpu - CC BY-SA 4.0