All you're talking about is taking your abstraction and calling it a "tool".
A basic abstraction remains untouched in the underlying implementation. It's just a call away.
There are semantic and technical differences between abstraction and meta-programming.
Macros are the least desirable "quality" of various LISPs if you ask me.
A suitable abstraction can always be built cleanly and efficiently without relying on the macro system.
We are so very far away from understanding the best approaches for writing software that solves problems.
> Building successive abstractions is the right way of doing things.
You seem to think that "code that writes code" only addresses problems that are solvable by building successive abstractions.
Most "code that writes code" that I've seen had nothing to do with implementing abstractions. Most were implementing code based on data, database-driven-code generation if you will.
To add some context, I'm in the "interpreters all the way down" camp. On other words, it seems to me that hardware vs software is an arbitrary distinction. After all, someone had to decide how the hardware would respond to code, in other words, someone had to physically program the hardware to behave a certain way. In that sense, isn't the code we write (in lisp, C, C++, etc...) just a way to meta-program the hardware?
I'm not sure if that was very clear... Another way of saying it is that simply declaring "software should never modify software" or "software should never be treated as data" is an arbitrary and almost meaningless declaration.
The primary concern of a problem should always be possible to solve without metaprogramming.
Metaprogramming is one of those things which sounds good but rapidly increases complexity beyond what is humanly manageable (even if you do it right i.e. LISP macros etc).
Building successive abstractions is the right way of doing things. I think even SICP tries to nail that into people's heads