> (but I don’t buy the argument that “real” macros are only able in s-exprs)
There are macros in non-s-expr languages. Usually then one needs to manipulate an AST data structure. That has consequences in those cases:
a) the macro forms need to be parsed according to a syntax, usually a predefined syntax
b) the AST data structure adds some complexity, which makes simple things more complex, but can also be a help
For Common Lisp a) means that the code enclosed in a macro does not need to conform to a predefined syntax and the macro is responsible to parse it. A typical example is the LOOP macro: (LOOP for i from a to b by 2 when (> a (foo b)) maximize (sin b)). That's a whole different sublanguage, which does not look like the typical nested lists based notation. It uses symbols as syntactic words in a complex syntax, which otherwise would not be valid Lisp.
You can see what it does: one can add arbitrary syntax. Which is good and bad at the same time.
b) means for Common Lisp that there is certain meta-level model, where the language can be used to program itself via procedural macros. Now one has to deal with code, where one can program on a meta-level which looks like the normal level. That's also good and bad. Good because one can apply all the knowledge and tools on the meta-level, too. Bad because it means that a programmer may see in a debugger code-generating code and the programmer sees code constructs which have code generators behind it. In a Lisp interpreter (one which processes Lisp source code at runtime), this will also be visible at runtime. In a language which uses AST data structures, the code in AST representation looks very different from the written code. In Lisp the code as data looks the same as the written code.
The experience is widely different from programming in other programming languages and not that easy to learn: programming a language on a meta-level in itself.
Most programming we see seems to happen without that self-programmability capability.
There are macros in non-s-expr languages. Usually then one needs to manipulate an AST data structure. That has consequences in those cases:
a) the macro forms need to be parsed according to a syntax, usually a predefined syntax
b) the AST data structure adds some complexity, which makes simple things more complex, but can also be a help
For Common Lisp a) means that the code enclosed in a macro does not need to conform to a predefined syntax and the macro is responsible to parse it. A typical example is the LOOP macro: (LOOP for i from a to b by 2 when (> a (foo b)) maximize (sin b)). That's a whole different sublanguage, which does not look like the typical nested lists based notation. It uses symbols as syntactic words in a complex syntax, which otherwise would not be valid Lisp.
You can see what it does: one can add arbitrary syntax. Which is good and bad at the same time.
b) means for Common Lisp that there is certain meta-level model, where the language can be used to program itself via procedural macros. Now one has to deal with code, where one can program on a meta-level which looks like the normal level. That's also good and bad. Good because one can apply all the knowledge and tools on the meta-level, too. Bad because it means that a programmer may see in a debugger code-generating code and the programmer sees code constructs which have code generators behind it. In a Lisp interpreter (one which processes Lisp source code at runtime), this will also be visible at runtime. In a language which uses AST data structures, the code in AST representation looks very different from the written code. In Lisp the code as data looks the same as the written code.
The experience is widely different from programming in other programming languages and not that easy to learn: programming a language on a meta-level in itself.
Most programming we see seems to happen without that self-programmability capability.