3 ms·
I've used macros for years, decades ago. I don't like them, and I was disappointed to see them so widely employed by Rust. I've written macros in asm, in TeX,
by todd8 5y ago
I've used macros for years, decades ago. I don't like them, and I was disappointed to see them so widely employed by Rust.
I've written macros in asm, in TeX, in Lisp, and even in TRAC (a macro based programming language invented by Calvin Moores in the 60's, see [1]). The most impressive macros I've used are found in the fantastic LaTeX graphics package named tikz. The manual for tikz is 391 pages long. This amazing graphics package is implemented in LaTeX and TeX macros.
So if tikz is so great, what's wrong with macros? The tikz package is great in spite of being implemented in macros. Looking over its implementation, I'm stunned that its developers were able to achieve it.
Macros are used because they abstract a mechanism that programmers would like to use, but macros easily become leaky abstractions. They can easily have semantics that depend on or affect other program elements that are not visible to the programmer. Macros can be written using other macros; macros can define new macros. Consequently, even simple macro systems (like the C preprocessor) are capable of doing any kind of a computation [3]. These machinations are not visible to the programmer using the macros so macros make accurate reasoning about the correctness of a program more difficult in the same way that subroutines having unrestricted access to global variables make correctness harder to (informally) verify.
Abstraction has proven to be an essential means for constructing programs. No one wants to program in an environment where goto's in a flat namespace replace all functions. I feel like macros are a flawed mechanism for abstraction; I would rather see the language syntax expanded to accommodate the desired features than to have them implemented via macros.
As impressive as the capabilities of macros are [4], wouldn't it be better to rely on functions as the primary abstraction mechanism used by the language's programmers.
Am I wrong?
[1] https://dl.acm.org/doi/10.1145/365230.365270 https://dl.acm.org/doi/10.1145/365230.365270
[2] http://www.bu.edu/math/files/2013/08/tikzpgfmanual.pdf http://www.bu.edu/math/files/2013/08/tikzpgfmanual.pdf
[3] https://stackoverflow.com/questions/3136686/is-the-c99-preprocessor-turing-complete https://stackoverflow.com/questions/3136686/is-the-c99-prepr...
[4] https://letoverlambda.com https://letoverlambda.com
- andrewaylett 5y agoRegular Rust macros are hygienic, unlike macros in many (but definitely not all) other languages. This means that a macro expands in place in the AST, and can only operate on its inputs. The C preprocessor, by comparison, operates on text and the expanded macro can trivially interact with syntactic elements around it once expanded. Procedural macros can run arbitrary rust code and can therefore do anything, but in general they don't -- and if you work within the interface you're given then the result will be hygienic. There's a sense in which macro hygiene is analogous to Rust's safety guarantees -- the compiler will normally guarantee the abstraction, but there's an escape hatch where we rely on the person implementing the code that uses unsafe or implementing a procedural macro to not break the abstraction for their callers. A leaky abstraction is considered to be a bug.