5 ms·
For me, lisp macros can be a bit vexing, I prefer to not have too many unique, one of a kind, meta-abstractions cooked up by teammates on a big project. A good,
by todd8 6y ago
For me, lisp macros can be a bit vexing, I prefer to not have too many unique, one of a kind, meta-abstractions cooked up by teammates on a big project. A good, widely adopted lisp macro can be handy, for example John Wiegley‘s use-package makes my Emacs configuration much cleaner. I’m not sure where the right boundary is.
Decades ago I was quite enthusiastic about macro systems, but the experience of TeX programming made me realize that sometimes plain old functional or applicative abstractions are better than macro based ones no matter how domain specific they are.
- TeMPOraL 6y agoFor "internal" macros, I think it's important to stop thinking about them as magical abstractions. It's better to instead approach both constructing and using them as an exercise in making invalid states not expressible. A macro lets you build a domain-specific abstraction that not only is extremely readable (matching the domain), but also ensures you just can't produce things that don't make sense domain-wise. In this way, such abstractions are much better than boilerplate you'd have to do with classical languages (though Haskell-like languages can probably get a similar effect through judicious use of types).
- deleted 6y ago[deleted]
- KineticLensman 6y ago> much better than boilerplate you'd have to do with classical languages Except that over-enthusiastic use of macros replaces well-understood idiomatic boilerplate with project-specific magic. (disclaimer - I love macros, used sparingly)
- AnimalMuppet 6y agoI've been wondering this for a while, and this seems like a reasonable time to ask it: If I understand correctly, you use macros to change the syntax of Lisp. But if I'm trying to write a DSL, why would I need new syntax? Why aren't new functions and data structures enough?
- aidenn0 6y agoYou usually use macros to change the semantics of Lisp: (foo (bar) (baz)) If foo is a function, then (bar) will be evaluated, (baz) will be evaluated and then (foo X Y) will be evaluated where X and Y are the results of evaluating (bar) and (baz). If foo is a macro, then none of the above is necessarily true. Macros let you implement new control-flow constructs, which may be necessary for a DSL (or at least a low-boilerplate DSL; you can always make ugly control-flow by wrapping every expression in a lambda, but the idea is to make something easier to write, not something harder to write)
- guenthert 6y agoIdeally, you make something easier to read.
- kazinator 6y agoIf you're just writing new functions and data structures, then you're not trying to write a DSL, but avoiding doing so. Why the functions and structures might not be enough is that there may be situations where their direct use is too difficult or error-prone somehow. It can make sense to start with the new functions and structures first and then see if some usage pattern calls out "I want to be a new macro".
- AnimalMuppet 6y agoHmm. That fits with advice from Extreme Programming: "Pay attention to pain". When it's hard (or even tedious) to write something, pay attention to that. It's trying to tell you something.
- TeMPOraL 6y agoThat's usually the advice given to aspiring macro writers. You write ordinary code, but when you start feeling there's a higher-level concept there for which you repeatedly write the same boilerplate, that's when you consider using macro to introduce said concept as a first-class, explicit thing. It's not that different than abstracting through functions or objects, but it allows you to abstract away the repetitive code structure as well.
- aidenn0 6y agoIt's a fairly well accepted recommendation to not use a macro when a function will do[1]. Everything bad people say about macros applies equally to functions, the dial is just turned up a bit; they both abstract behaviors by hiding their implementations. The advantages is that this reduces mental load when reading code; imagine if you had to parse a block of code and say "oh yeah, that's just a simple median of 3 quick sort" every time instead of just (sort ...). That would increase your mental load both when reading and writing. On the other hand, when something goes wrong in sort (even if it's not a bug in sort itself, maybe some garbage was passed in), the fact that it's a function call actually increases the mental load when debugging. Good tooling that lets you print stack frames and such really improves things. The same is true for macros, but things are worse in both directions since macros are more powerful. You can write better abstractions to decrease the mental load even more, but when things go wrong, more things can go wrong because macros are less constrained than functions. Again, good tooling can go a long way to reducing the debugging pain. Stepwise macro expansion is a big win and being able to do it in-place is even better. 1: With the exception that lispers will use macros to prevent requiring explicit lambdas. For example, the WITH-FOO macros common in lisp can all be written using lambdas, and it would even be idiomatic to do so in many functional languages. From what I can tell this is originally because of the extra computational expenses of using lambdas, but persists because the syntax is more uniform with LET and friends