For many domains, being able to implement a domain-specific language with a set of expressive rules for the domain is an incredible productivity boost and also prevents many mistakes because you cannot represent them.
Not being able to manipulate the AST means that you are restricted on the embedded DSLs that you can provide.
And embedded DSLs encompasses code generation for:
- state machines
- parsing grammars (PEGs for example)
- shader languages
- numerical computing (i.e. having more math like syntax to manipulate indices)
- deep learning (neural network DSL)
- HTML templates / generators
- monad composition
There is a reason most people are not building in Assembly anymore, there is a right-level of abstraction for every domain.
A language that provides building block for domain expert to build up to the right level of abstraction is very valuable.
Low-level languages (aka "systems programming" languages) already suffer from various constraints that increase their accidental complexity. Is it really necessary to complicate those particular languages further to support embedded DSLs?
I don't think there's a universal right or wrong answer here, but there is certainly a big question.
Ironically, the lack of macros leads to an explosion of ad-hoc, extra-language DSLs. Look at rules engines, for example. In Drools (java), you have to write rules with a special language, DRL. Meanwhile in Clara (clojure) you write your rules in clojure. Macros simplify languages, they don't complicate them.
It's always a trade-off and placing the cursor correctly is tricky. Things like macros, operator overloading, metaprograming, virtual calls, exceptions or even function pointers are effectively "obfuscating" code by having non-obvious side effects if you don't have the full context. On the other hand if you push the idea too far in the other direction you end up with basically assembly, where you have an ultra-explicit sequence of instruction to execute.
It's very easy to come up with examples of terrible abuse of these features that lead to bad code (like for instance if somebody was insane enough to overload the binary shift operator << to, I don't know, write to an output or something) but it also gives a lot of power to write concise and expressive code.