Frage

Let's define a function, the body of which contains the macro, which will be expanded at some unspecified time and will use a global dynamic value of *test* during this process.

> (defvar *test* nil)
> (defmacro body ()
    `(print ,*test*))
> (defun test ()
    (body))
> (test)
NIL

But what if I want to bind *test* to, say, 1 during function definition, so that the macroexpansion operated with this binding in effect and the call to test produced 1 instead of NIL.

Just wrapping defun in let doesn't work:

> (let ((*test* 1))
    (defun test ()
      (body)))
> (test)
NIL

Probably, it is related to this line in Hyperspec:

defun is not required to perform any compile-time side effects

But are there any other ways to do that?

War es hilfreich?

Lösung

As you yourself write, macro are expanded at unspecified time. In my SBCL the macro is expanded before the whole form is evaluated, which means before the LET binding is in effect. For some interpreters the macro might be expanded when the function is executed, after the binding expired.

Early versions of what became Common Lisp included such mechanism through COMPILER-LET, but it has been removed. See COMPILER-LET-CONFUSION issue for more detail. Lexically some of the effects can be achieved using MACROLET/SYMBOL-MACROLET. Dynamically it is difficult to make this work sanely and I would recommend rethinking the approach, if using actual dynamic bindings seem necessary.

Andere Tipps

I think it because *test* variable is valid within the body of the let.

You could introduce a let like this:

(defvar *test* nil)

(defmacro foo ()
  (let ((test (gensym)))
    `(let ((,test *test*))
       (print ,test))))

(defun test-foo ()
  (foo))

(test-foo) => print and returns NIL
(let ((*test* 1))
  (test-foo)) => print and returns 1

What about controlling the time of evaluation by using a macro instead of let (here it is assigning a known value to a known variable, but it could easily be expanded to handle more variables since we're playing with dynamic variables):

(defmacro letter (&body body)
  (let ((old-test *test*))
    (set '*test* 1)
    `(progn
       ,@body
       (set '*test* ,old-test))))

Defining test:

(letter (defun test () (body)))

Using test:

CL-USER> (test)

1 
1

This seems to work as expected on SBCL, need to get some sleep before trying it on other implementations.

Hmmm, macro-expanding makes it obvious that letter works correctly only when macro-expanded AND EVALed. Simply macro-expanding doesn't restore *test* to its old value (doh). So it's not a good 'binding emulator'.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top