Dart is a statically typed language and we wanted macros to be able to introspect over the semantics of the code the macro is applied to, not just the semantics. For example, if a macro is generating code for serialization, we wanted the macro to be able to ask "Does the type of this field implement JsonSerializable?". Answering that means being able to look up the type of the field, possibly walk its inheritance hierarchy, etc.
It's a very different problem from just "give me a way to add pretty loop syntax".
This holds for "old" lisps. There are other options. Racket and Scheme uses "syntax objects". Syntax objects contain besides the old syntax tree also source location information and lexical information.
See for example the last part of:
https://parentheticallyspeaking.org/articles/bicameral-not-h...
[0]: imagine your colleague wrote a macro that redefines for loops because at the time, it made life easier for him.
This is like asking "what if your coworker named all errs as `ok`" so everything was `if ok { return errors.New("Not ok!!"); }`. It's possible but no one does it.
This is why `defmacro` and `gensym` in common lisp are awesome, and similarly why Go's warts don't matter. Much of programming language ugliness is an "impact x frequency" calculation, rather than one or the other.
It's also why javascript is so terrible, you run into it's warts constantly all day long.
But apparently lots of other people do run into them regularly, so I believe that such things do exist.
By the same token, I've heard countless reports of people struggling with the flexibility that Lisp offers, with co-workers who abuse it to create nightmarish situations. That you haven't experienced that doesn't mean no one does.
You wouldn't let a child handle a chainsaw.
I don't mean "do dumb stuff", I mean I've literally never seen anyone redefine the `define` keyword in any code.
With javascript, I do see people use `===` frequently. It's a wart of the language that the operator even exist. It's not "dumb" to use it - it's how frequently are you assaulted with the bugs of the language (not bugs in your code).
But I don't recall seeing someone re-define `define` in real life.
Nor do I recall seeing any problematic redefinitions in Scheme in real life.
That said, if you wanted to make a language variant in which `define` did something different than normal (say, for instrumentation, or for different semantics for variable syntax), then you'd probably use Racket, and you'd probably define your own `#lang`, so that the first line of files using your language would be something like `#lang myfunkylang`.
You can randomly sample code in <https://pkgs.racket-lang.org/>, and look for people doing anything other than `#lang racket/base` or `#lang typed/racket/base`.