From Google's Common Lisp style guide ( https://google.github.io/styleguide/lispguide.xml#Macros ):
You should follow the so-called CALL-WITH style when it
applies. This style is explained at length in
http://random-state.net/log/3390120648.html. The general
principle is that the macro is strictly limited to
processing the syntax, and as much of the semantics as
possible is kept in normal functions. Therefore, a macro
WITH-FOO is often limited to generating a call to an
auxiliary function CALL-WITH-FOO with arguments deduced
from the macro arguments. Macro &body arguments are
typically wrapped into a lambda expression of which they
become the body, which is passed as one of the arguments of
the auxiliary function.One of the problems here is that the function does get an anonymous function passed. That means the function can provide dynamic scope via constructs like CATCH, LET with dynamic bindings, UNWIND-PROTECT, and so on. But it can't rewrite any code in the passed function.
This can only be done in a macro.
> A good Lisp programmer would never write such inefficient code. Why create a local function here when you can easily write a macro that expands into a loop with expr as its body?
The nice thing about the call-with style is that it makes it easier to redefine your macro's functionality without recompiling all the call sites.
And hey, most of my Scheme code barely has any macros (so call my idioms a decade out of date, we have a macro system equivalent to CL's, and I've never felt much need to use it. And yes, most Schemes do have a CL-equivalent macro system, it's just nonstandard), and I still love lisp.
Lisp has many, many, many, cool advantages, but I still think never having to worry about syntax or indenting style is the greatest one: people underestimate just how much the little things matter. :-)
Look at this example from the article:
(defmacro with-each-natural-number (n expr) `(each-natural-number (lambda (,n) ,expr)))
It assumes that the programmer already wrote a function each-natural-number that does the required functionality when another function is passed into it. That's the Ruby way, because it only has functions, not macros. A good Lisp programmer would never write such inefficient code. Why create a local function here when you can easily write a macro that expands into a loop with expr as its body?
So, indeed, if you write in Lisp as if it were Ruby, then Ruby is an acceptable Lisp. If you write in Lisp as it's supposed to be written, then Ruby is ridiculously restrictive, and slow.