In OOP languages such as Smalltalk, there is much less need for macros.
The author's point is that programming languages are great when you're cutting with the grain, when you're working in the domain the author optimized the language around. But when you leave the domain, macros are an important tool.
Don't get me wrong, I think that lazy evaluation is an incredibly valuable tool for separating what from how. But I wonder how it obviates the need for macros any more so than a well-defined OO system or any other non-trivial language feature.
However, you need monads to express other patterns of control flow (exceptions and dynamic scoping, for example) that are possible with macros. The problem is that you have to do everything through bind. Macros don't have that restriction.
But the one thing that makes macros irreplaceable is the fact that they can operate at compile-time. This is what I meant to emphasize when I wrote about automating programming.
> But the one thing that makes macros irreplaceable is the fact that they can operate at compile-time.
"Sufficiently smart compiler" is the standard strawman argument. It's not a sufficient response, but it is actually pretty astounding what can be handled at compile time.
Smalltalk has always done iteration this way with blocks (closures), and because of the syntax it works and looks nice.
Of course this needs the "sufficiently smart compiler" to be efficient. But there's no way to program domain-specific knowledge into the compiler like you can with macros. For example Gerald Sussman has done a lot of work in optimizing numerical code symbolically (http://repository.readscheme.org/ftp/papers/berlinsurati-pep...), I think _On Lisp_ has a chapter about doing partial evaluation on Bezier curves, and there's some regex-specific tricks in CL-PPCRE that I don't think can be matched by generic partial evaluation.
I loved the flexibility they allowed when I first used them, but these days they often feel to me like going down a level in that more needs to be expressed. Yeah, if your tools can't efficiently handle your current case, that escape hatch is incredibly valuable.
You can. It just won't be as syntactically elegant. Use expression trees and code emission the language exposes. The optimization happens at run-time, rather than compile time. But you'll actually, in most cases, get more optimization opportunity.
Monads are just a generalization of a commonly recurring pattern.
All imperative languages can be said to have "monads" in the sense of having a "semicolon operator" (in some languages, that's just newline) that is the monadic bind. You're programming in one "ambient monad" whose power is fixed by the language itself.
Haskell has an overridable semicolon, and that gives you a lot of DSL power. Instead of one "ambient monad", you have lots of user-defined monads useful for different purposes.
Besides overridable semicolons, monads are just a plain useful abstraction of data structures.
Monads are incredibly useful in a huge variety of programming tasks (non-determinism, effects, exceptions, parsing, and much more). They make Haskell code utilizing them far more broadly useful than code I've seen in any other language.