CL allows us to write code the way we think about a problem--and then bring life to that way of framing the problem. We can come up with an ideal way of describing a solution and then make a language work that way. I say a language because the target of our code might be C or JavaScript (these days that is more often the case for me than targeting CL itself, cf. 4500 recent lines of Lisp that turns into 8000 lines of terse JavaScript).

Our ability to reason correctly about systems directly corresponds to how complex they are. And I posit that complexity in code is best measured in number of symbols (because lines can be arbitrarily long, and longer symbol names can actually be helpful). So a system that reduces the number of symbols necessary to express a solution increases the size of a solution about which we can successfully reason. Just as computers are a "bicycle for the mind", homoiconicity+macros (of which I posit CL is still the best practical implementation) is a "bicycle for the programmer's mind".

Lisp provides an optimal solution for thinking of programs as nested sequences of arbitrary symbols. Sequences that can be transformed (and must be, for a computer to evaluate them, unless we hand-write machine code!). Common Lisp provides an optimal set of built-in operators for composing operations and transformations on its fundamental data types (atoms/cells/lists/trees). Other languages might provide better implementations of particular paradigms or whatever, but CL is the best language for implementing macros. Other Lisps make macros "safer" and miss the point.

As Vladimir Sedach wrote earlier on Hacker News[1]:

"The entire point of programming is automation. The question that immediately comes to mind after you learn this fact is - why not program a computer to program itself? Macros are a simple mechanism for generating code, in other words, automating programming. Unless your system includes a better mechanism for automating programming (so far, I have not seen any such mechanisms), _not_ having macros means that you basically don't understand _why_ you are writing code.

This is why it is not surprising that most software sucks - a lot of programmers only have a very shallow understanding of why they are programming. Even many hackers just hack because it's fun. So is masturbation.

This is also the reason why functional programming languages ignore macros. The people behind them are not interested in programming automation. Wadler created ML to help automate proofs. The Haskell gang is primarily interested in advancing applied type theory.

Which brings me to my last point: as you probably know, the reputation of the functional programming people as intelligent is not baseless. You don't need macros if you know what you are doing (your domain), and your system is already targeted at your domain. Adding macros to ML will have no impact on its usefulness for building theorem provers. You can't make APL or Matlab better languages for working with arrays by adding macros. But as soon as you need to express new domain concepts in a language that does not natively support them, macros become essential to maintaining good, concise code. This IMO is the largest missing piece in most projects based around domain-driven design."

[1] https://news.ycombinator.com/item?id=645338