IME, the primary use case for macros in Lisps is language extension, a step before a DSL. For every `loop` there are thousands of `with-x` macro implementations. See the macro-writing macros in Alexandria/Serapeum for the most common patterns.
Also, some dynamic languages (Io, Prolog, Groovy) support DSLs through non-macro mechanisms. DSLs in those languages are as easy (or easier) to implement as in Lisps; however, they tend to be less performant. So, again, compile-time macro execution does matter for the DSL use case, too.
The characterization of it not being "compile-time macro execution" is strange, what do you think these predicates are doing?
I have some reading to do, but overall it seems like macros are favored instead of fexprs. Macros completely avoid some environment handling issues.
> Messages as Code — Messages form trees that can be inspected and rewritten at runtime. Argument evaluation can be deferred, so if, while, and for are implementable in Io itself.
Sure but you've also got CTFE in languages that lack a proper macro system such as C++. The defining characteristic (IMO) is the access to and ease of manipulating the AST on the fly. And any time you're outputting an AST you're going to need access to the compiler at which point the line between run time and compile time becomes blurred and arbitrarily nested.
You can create DSLs without macros, for example by parsing and interpreting at runtime, but macros let you move much of that logic to compile time.
For me, that's the killer feature: writing code that looks dynamic or interpreted, but ends up as compiled code.