Why does this Lisp macro generally work, although every part doesn't work?

I read / work through Practical Common Lisp. I am in the chapter on creating a test framework in Lisp.

I have a function "test- +" implemented below, and it works:

(defun test-+ () (check (= (+ 1 2) 3) (= (+ 5 6) 11) (= (+ -1 -6) -7))) 

Remember, I said it works, that's why what follows is so perplexing ....

Here is the code that "test- +" means:

 (defmacro check (&body forms) `(combine-results ,@(loop for f in forms collect `(report-result ,f ',f)))) (defmacro combine-results (&body forms) (with-gensyms (result) `(let ((,result t)) ,@(loop for f in forms collect `(unless ,f (setf ,result nil))) ,result))) (defmacro with-gensyms ((&rest names) &body body) `(let ,(loop for n in names collect `(,n (gensym))) ,@body)) (defun report-result (value form) (format t "~:[FAIL~;pass~] ... ~a~%" value form) value) 

Now what I was doing is using Slime for macro distribution, step by step (using ctrl-c RET, which maps to macro-instance-1).

So, the call to "check" "test- +" expands to this:

 (COMBINE-RESULTS (REPORT-RESULT (= (+ 1 2) 3) '(= (+ 1 2) 3)) (REPORT-RESULT (= (+ 5 6) 11) '(= (+ 5 6) 11)) (REPORT-RESULT (= (+ -1 -6) -7) '(= (+ -1 -6) -7))) 

And then this macro expands to this:

 (LET ((#:G2867 T)) (UNLESS (REPORT-RESULT (= (+ 1 2) 3) '(= (+ 1 2) 3)) (SETF #:G2867 NIL)) (UNLESS (REPORT-RESULT (= (+ 5 6) 11) '(= (+ 5 6) 11)) (SETF #:G2867 NIL)) (UNLESS (REPORT-RESULT (= (+ -1 -6) -7) '(= (+ -1 -6) -7)) (SETF #:G2867 NIL)) #:G2867) 

And this is the TO code, right on this sentence, which does not work. If I insert this into the REPL, I get the following error (I use Clozure Common Lisp):

Unbound variable: #: G2867 [UNBOUND-VARIABLE type condition]

Now, if I take the same code, replace gensym with a variable name like "x", it works fine.

So, how can we explain the following surprises:

  • The "test- +" macro that causes all this works fine.

  • The macro volume of the Combined Results macro is not executed.

  • If I remove gensym from the "comb-results" macro decomposition, it really works.

The only thing I can guess is that you cannot use code that contains literary uses of gensyms. If so, why not, and how does it work? And if this is not an explanation, what is it?

Thanks.

+6
source share
2 answers

The code, after printing and reading, is no longer the same code. In particular, two instances of #:G2867 in the printed representation will be considered two separated characters (albeit with a common name), while they must be the same in the original internal representation.

Try setting *PRINT-CIRCLE* to T to save the identifier in the printed code representation with the extended macro.

+11
source

GENSYM creates non-integer characters. When a macro works fine, this is not a problem, because the same uninterrupted character is replaced in the entire expression.

But when you copy and paste the expression into the REPL, this does not happen. #: tells the reader to return a character without a character. As a result, each occurrence of #:G2867 is a different character, and you get a warning about an unbound variable.

If you do (setq *print-circle* t) before executing MACROEXPAND, it will use the notation #n= and #n# to associate identical characters together.

+11
source

Source: https://habr.com/ru/post/926596/


All Articles