The short answer is: Don't do that!
You have no guarantees on the number of times a macro will be expanded. Implementations are allowed flexibility in the number of times a macro-expander function is executed, interpreter-based implementations/modes only store the raw lists, so under that regime an expander function
must be executed for every single time it shows up in the execution trace at runtime
I'm not entirely sure why it is happening, but AFAIR, the repl has some magic where it will interpret some forms rather than compiling-then-invoking, and it might be trying both.
If you try:
you get two prints, but if you force the interpreter:
Code: Select all
(let ((*evaluator-mode* :interpret)) (eval '(hell)))
then you only get one. Likewise, if you force the compiler:
Code: Select all
(funcall (compile nil '(lambda () (hell)))
you also only get one print.
OTOH it may be a bug, but the code produced AFTER the expansion is only ever being run once, which is the important bit.
Now, back to the general point, relying on side-effects in macro-expander functions is the wrong thing, as I said above the expander may be run all over the place, but also, if your code is compiled and saved to a fasl file, then when you load the fasl into a clean image the expander will never be run at all! I was bitten by this in my html-generation library (which I
still need to upload to github

) , I was saving some information about each tag into a hash table from the macro-function and then expecting that data to be available and it wasn't.
I changed the macro to expand into a PROGN that included the necessary SETF (along with the original expansion) and all was well.