I think the reason that Lisp metaprogramming works so well is just that Lisp basically has no syntax and the simplest reasonable grammar. Lisp syntax is just the AST with nested brackets. This means that modifying the Lisp syntax structure is equivalent to modifying the AST.
Fundamentally, you can make accessing the AST as easy as possible, but at the end of the day you're still accessing the AST so you can change the AST. The more complicated the AST, the more complicated the change will need to be.
But if you actually want to walk recursively down an AST, the fact that it's hard to get a usable AST in Lisp makes metaprogramming difficult and error-prone. For many uses, I don't really want the source code to a Lisp fragment, but the AST, in the sense of a semantically marked-up fragment that's resolved variable bindings and done all the other things a parser does.
Implementing a correct code-walker for Common Lisp is... really nontrivial. The best available one I've found is in Arnesi, http://common-lisp.net/project/bese/docs/arnesi/html/A_0020C... Whereas walking a Haskell AST fragment is trivial.
For all the meta-programming projects I've taken on - delimited continuations, embedded logic programming, and extensible pattern matching - I'll take Lisp data structures over the actual Lisp AST any day.
It occurs to me that most of what made/makes Lisp so powerful historically (expression-based and everything-is-an-object, for example) is probably quite different from what differentiates it today (which probably has more to do with the assumptions that Lisps don't force on you)
Consider the Julia language. Julia is homoiconic -- Julia code is made of Julia data structures: http://julialang.org/manual/metaprogramming/
Imagine for a second that Julia didn't have the :() quote form, and if you wanted to build up some syntax you had to use constructors, something like this:
a + b - c
This would still make Julia homoiconic, but the different between "using" the expression (the first line) and "mentioning" it (the horrible Expr thing) is much greater.
Clojure does have more syntax than Common Lisp, but its syntax is still "near at hand" because it's still made up of the things you use every day. Functions use  for arguments, but that's just a vector which you use anyway. There's not really much syntax in Clojure where "mentioning" it is harder than "using" it.