I\'m used to lazy evaluation from Haskell, and find myself getting irritated with eager-by-default languages now that I\'ve used lazy evaluation properly. This is actually q
Macros can be used to handle lazy evaluation, but's just part of it. The main point of macros is that thanks to them basically nothing is fixed in the language.
If programming is like playing with LEGO bricks, with macros you can also change the shape of the bricks or the material they're built with.
Macros is more than just delayed evaluation. That was available as fexpr (a macro precursor in the history of lisp). Macros is about program rewriting, where fexpr is just a special case...
As an example consider that I'm writing in my spare time a tiny lisp to javascript compiler and originally (in the javascript kernel) I only had lambda with support for &rest arguments. Now there's support for keyword arguments and that because I redefined what lambda means in lisp itself.
I can now write:
(defun foo (x y &key (z 12) w) ...)
and call the function with
(foo 12 34 :w 56)
When executing that call, in the function body the w parameter will be bound to 56 and the z parameter to 12 because it wasn't passed. I'll also get a runtime error if an unsupported keyword argument is passed to the function. I could even add some compile-time check support by redefining what compiling an expressions means (i.e. adding checks if "static" function call forms are passing the correct parameters to functions).
The central point is that the original (kernel) language did not have support for keyword arguments at all, and I was able to add it using the language itself. The result is exactly like if it was there from the beginning; it's simply part of the language.
Syntax is important (even if it's technically possible to just use a turing machine). Syntax shapes the thoughts you have. Macros (and read macros) give you total control on the syntax.
A key point is that code-rewriting code is not using a crippled dumbed down brainf**k-like language as C++ template metaprogramming (where just making an if is an amazing accomplishment), or with a an even dumber less-than-regexp substitution engine like C preprocessor.
Code-rewriting code uses the same full-blown (and extensible) language. It's lisp all the way down ;-)
Sure writing macros is harder than writing regular code; but it's an "essential complexity" of the problem, not an artificial complexity because you're forced to use a dumb half-language like with C++ metaprogramming.
Writing macros is harder because code is a complex thing and when writing macros you write complex things that build complex things themselves. It's even not so uncommon to go up one level more and write macro-generating macros (that's where the old lisp joke of "I'm writing code that writes code that writes code that I'm being paid for" comes from).
But macro power is simply boundless.