Yesterday I wrote an article describing one of the ways traditional Lisp macros can be unhygienic even when they appear to be hygienic. Here’s a horrible solution to that.
The problem I described is that the expansion of a macro can refer to the values (usually the function values) of names, which the user of the macro can bind, causing the macro to fail. So, given a function
(defun call-with-foo (thunk) ... (funcall thunk))
Then the macro layer on top of it
(defmacro with-foo (&body forms) `(call-with-foo (lambda () ,@forms)))
is not hygienic so long as local functions named
call-with-foo are allowed:
(flet ((call-with-foo (...) ...)) (with-foo ...))
The sensible solution to this is to say, just as the standard does about symbols in the
CL package that you are not allowed to do that.
Here’s another solution:
(defmacro with-foo (&body forms) `(funcall (symbol-function 'call-with-foo) (lambda () ,@forms)))
This is robust against anything short of top-level redefinition of
call-with-foo. And you can be mostly robust even against that:
(defmacro with-foo (&body forms) `(funcall (load-time-value (symbol-function 'call-with-foo)) (lambda () ,@forms)))
This still isn’t safe against really malignant users, since the load time of the macro’s definition and its uses are not generally the same. But it’s probably fairly good.
I hope I never feel I have to use techniques like this.