Just when I thought I had a pretty good handle on macros, I came across the source for some
which looked a bit odd to me at first glance.
(defn some
[pred coll]
(when (seq coll)
(or (pred (first coll)) (recur pred (next coll)))))
My first instinct was that seems like it would be stack consuming, but then I remembered: "No, dummy, or
is a macro so it would simply expand into a ton of nested ifs
".
However mulling it over a bit more I ended up thinking myself in a corner. At expansion time the function source would look like this:
(defn some
[pred coll]
(when (seq coll)
(let [or__4469__auto__ (pred (first coll))]
(if or__4469__auto__
or__4469__auto__
(recur pred (next coll))))))
Now what's got me confused is that final recur
call. I've always thought that macroexpansion occurs prior to runtime, yet here you have to actually call the already expanded code at runtime in order for the second macroexp .... wait a second, I think i just figured it out.
There is no second macroexpansion, there are no nested if
blocks, only the one if
block. The call to recur
just keeps rebinding pred
and coll
but the same single block above keeps testing for truth until it finds it, or the collection runs out and nil
is returned.
Can someone confirm if this is a correct interpretation? I had initially confused myself thinking that there would be an interleaving of macroexpansion and runtime wherein at runtime the call to recur would somehow result in a new macro call, which didn't make sense since macroexpansion must occur prior to runtime. Now I think I see where my confusion was, there is only ever one macro expansion and the resulting code is used over and over in a loop.
To start with, note that any function can serve as an implicit loop
expression. Also, recur
works just like a recursive function call, except it does not use up the stack because of a compiler trick (that is why loop
& recur
are "special forms" - they don't follow the rules of normal functions).
Also, remember that when
is a macro that expands into an if
expression.
Having said all that, you did reach the correct conclusion.