This is a related question, some sort of a follow up.
Let's say I'm trying to build a loop expression by using macros, in which the resulting loop expression is dependant on whether the parameter is a list:
(defmacro testing-loop (var)
`(eval (append '(loop for x from 0 to 5)
(when (consp ,var) '(and y in ,var))
'(collect)
(if (consp ,var) '(y) '(x))))
This seems to work:
CL-USER> (testing-loop 2)
(0 1 2 3 4 5)
CL-USER> (testing-loop (list 5 6 7))
(5 6 7)
But when applying this macro in a lexical closure, it breaks down:
CL-USER> (let ((bar (list 1 2 3)))
(testing-loop bar))
which throws undefined variable: BAR
I expected testing-loop
to macroexpand into the lexical scope where bar is bound?
@mck, I see why you want to use
eval
now. But it's a very messy solution, and slow, as I mentioned in my answer to the previous question. The classic On Lisp says this abouteval
:"Generally it is not a good idea to call eval at runtime, for two reasons:
It’s inefficient: eval is handed a raw list, and either has to compile it on the spot, or evaluate it in an interpreter. Either way is slower than compiling the code beforehand, and just calling it.
It’s less powerful, because the expression is evaluated with no lexical context. Among other things, this means that you can’t refer to ordinary variables visible outside the expression being evaluated.
Usually, calling eval explicitly is like buying something in an airport gift-shop. Having waited till the last moment, you have to pay high prices for a limited selection of second-rate goods."
In this case the simplest thing is just to:
I know you want to factor out the common
loop for x from 0 to 5
(which isn't actually needed in the second branch anyways). Butloop
is itself a macro which is converted at compile time to efficient, low level code. So the call toloop
has to be built at compile time, using values which are available at compile time. You can't just insert an(if)
in there which is intended to be evaluated at run time.If you really don't want to repeat
loop for x from 0 to 5
, you could do something like:That's just to give you the idea; if you really do this, make sure to
gensym
!A good lesson to learn from this is: when you are writing macros, you need to keep clearly in mind what is happening at compile time and what is happening at run time. The macro you wrote with
eval
compiles theloop
macro dynamically, every time it is run, based on the return value ofconsp
. You really want to compile the 2 differentloop
macros once, and just select the correct one at run-time.