What makes Lisp macros so special?
-- https://stackoverflow.com/questions/267862/what-makes-lisp-macros-so-special
-- https://www.quora.com/What-is-so-special-about-Lisps-macros
-- https://news.ycombinator.com/item?id=9508108
-- https://news.ycombinator.com/item?id=2839742
-- http://kresimirbojcic.com/2011/08/02/whats-so-special-about-lisp.html
The value of switching to lisp (or a variant of lisp) must come from the availability of macros. By looking at examples of lisp macros, I find their usefulness certainly not obvious nor necessarily compelling. It is examples of how to benefit from using a language's facility, that make programmers switch.
Paul Graham can keep touting Lisp macros for as long as he wants, without obvious examples, programmers will keep dismissing the switch as being not worth the switching cost.
5 comments
[ 0.30 ms ] story [ 37.4 ms ] threadThink about JavaScript before and after destructuring was implemented. How many lines of code and mental effort are saved!
In Common Lisp this is just a macro, DESTRUCTURING-BIND (http://www.lispworks.com/documentation/HyperSpec/Body/m_dest...). Not a fan of the syntax? Just write a macro to modify how you use it.
Macros allow you the programmer to do similar things, but with anything you want. You don't have to wait for a language committee to agree on something, and then a third party to write a convoluted pre-processing system to 'transpile' the code to work. It's built into the language.
I also hear good things about Paradigms of AI Programming, but I haven't read it yet. From Peter Norvig's retro on the book[2], here are the lessons in it which involve macros:
Design patterns can be used informally, or can be abstracted into a formal function, macro, or data type (often involving higher-order functions). [p. 177]
We can write a compiler as a set of macros. [p. 277]
If you can understand how to write and when to use once-only, then you truly understand macros [p. 853]
A word to the wise: don't get carried away with macros [p. 855]
[0] http://www.paulgraham.com/onlisp.html [1] https://letoverlambda.com/index.cl/toc [2] https://norvig.com/Lisp-retro.html
But even beyond that I think the big purpose of macros is the ability to mold the language the application you're applying it to. This is the Domain Specific Language (DSL) idea.
For instance, take Protocol Buffers. The .proto format is a language that lets you define your messages and denote which fields are required, optional, etc. You run a compiler against these .proto files that spits out a generated library for the platform you're on.
This sort of workflow isn't as onerous as it used to be with all the CI/CD push to have tools that build your application in one step. But it is still a separate language with its own compiler that must be maintained entirely outside of your project or language ecosystem.
If, in a hypothetical universe where Lisp dominated Google, protocol buffers had been written in Lisp you wouldn't need a separate compiler and language. A library of macros like defpackage and defmessage could be written that would generate the code for sending and receiving each message type you define inline with the rest of your application. It would all be Lisp code and would benefit from all of your Lisp tools, like linting, debugging, and testing.
And if it was written in a Lisp-y tradition, such a DSL would likely be riddled with hooks for your application code to be able to attach to dynamically, say if you would like every receipt of a message to be logged or if a message fails validation you would want to page the on-call to figure out who's sending bad messages and why.
So, in short, macros allow you to extend the language for reasons even beyond syntactical conveniences like DESTRUCTURING-BIND. It allows you to really shape your tools to the application at hand, and allows others to shape the tools you share to their applications.
I was hooked by the list representation: how cons cells can be treated as immutable to implement list operations by always allocating new cells that are garbage collected.
I was hooked by the uniform syntax: good-bye ambiguities requiring associativity and precedence rules.
I was charmed by the empty list being nil which is also Boolean false, and the convenience of (car nil) just returning nil: the resulting code looked slick and elegant to me.
I thought it was great how you can just quote any part of the syntax and you get an expression which gives you that syntax as a piece of literal data.
Eventually I learned about defmacro, too, by which time I was productively using Lisp.
I learned about backquote before macros: wow, how great, I thought: like "foo $bar" string interpolation in the shell but with actual data structure.
I got into Lisp not because of some online hype about macros, but because I happened on a copy of Stuart Shapiro's Common Lisp: An Interactive Approach and worked through that. Now that book can be downloaded in electronic form.
One thing that charmed me was the Common Lisp read-table driven dispatch. Knuth's TeX does something very similar. I imitated Knuth's idea in a macro preprocessor that I wrote in 1999 called mpp; the preprocessor was able to redefine its read syntax by re-assigning the categories of characters. When I discovered that Lisp has a character-categorizing dispatch table as the basis of its scanning, I was really bowled over: it was something I had already liked to the point of having implemented it. (Today, I'm not so hot on read tables any more; my TXR Lisp doesn't have the feature.)