> Macros bring syntactic abstraction, which is a wonderful thing. It helps make your code clearer, by describing your intent without getting bogged in implementation details (indeed abstracting those details away). It helps make your code more concise and more readable, by eliminating both redundancy and irrelevant details. But it comes at a cost to the reader, which is learning a new syntactic concept for each macro. And so it should not be abused.
I really think this just applies to any kind of indirection - classes, functions, even named constants (vs literals).
Syntax changes have different consequences for reading comprehension. You can often skip over a function call, making only a reasonable guess at what the function does and relying on invariants for all function calls. A macro can arbitrarily change the lexical environment of anything contained in it with few constraints, so reading other parts of a file without knowing what each macro does is more precarious.
And when it comes to navigating large amounts of code, you do need to stop somewhere; you can't do a depth-first read of everything before doing anything.
> I really think this just applies to any kind of indirection - classes, functions, even named constants (vs literals).
Disagree. Macros are fundamentally different in that they can change the syntax of the language. As such, they can inhibit the code's readability in ways the other defining forms cannot. Looking up something by name in CL is as simple as meta-dot. But you cannot meta-dot syntax. (Well, you could on a Lisp Machine, but not so much on current CL implementations.)
It's a difference of degree, not of kind. Any indirection forces the user to reason about a constructed concept instead of the literal facts of what's happening. By introducing one, you're making the assertion that the abstract concept is easier to reason about (including any effort required to learn it) than the contents being abstracted away.
You can C-c M-e the macro form to have SLIME expand it for you inline (read-only), and then navigate around it, using e to expand and c to collapse back. Super useful.
I don't understand this? Besides the macrostep expander, meta-dot works on the macro name: reader macros _are_ harder to debug, but they're generally discouraged.
Right; all abstractions are only as good as they don't leak, but the question is "how easy is it to debug when it does leak?"
I think you listed classes, functions, and named constants in approximately the order of debugability too.
It can be unclear even which macroexpansions are in play from a backtrace, much less which one caused the breakage. (non inlined) functions are right there in the backtrace, and of course, debugging a named constant is as simple as typing its name into the REPL.
In emacs, the macrostep expander solves most of the Macro debugging issues: you usually can expand a macro use, and see exactly what code is being generated or “refactor” the macro away by copying the expansion at a certain level of detail and replacing the original form with it.
Yes, this is a useful tool; I still maintain that debugging macros with this tool is harder than debugging functions with the various other SLIME tools.
Indeed, for what are classes and functions but specific kinds of macro (roughly speaking); or, macros and classes as special kinds of functions..
I'd include overloading operators in that list. It can be convenient, but comes at a cost to newcomers to the codebase.
I suppose any kind of shortcut or abbreviation carries this risk, to increase the cognitive load of the reader - things they have to remember and mentally substitute the shortcuts until they become second nature.
(Oh, right, what we call "shortcut" and "indirection" are both examples of abstraction, its value and cost.)
If you don't define the macro, you will have to replace it with boilerplate everywhere you would have used it, and constantly rereading all that is more ongoing grunt work than learning what the macro does.
I really think this just applies to any kind of indirection - classes, functions, even named constants (vs literals).