Reputation: 7784
I'm writing a small special-purpose language in Common Lisp using a defmacro. I cannot figure out the proper backquote procedure to have a variable defined in a top-level let statement, shadowed in a nested macrolet, and returned in a nested labels, all point to the same symbol.
Here's some small example code that shows the problem:
(defmacro with-keylang (&body body)
(let ((keyblank-size-x (gensym)))
`(let ((,keyblank-size-x 42))
(labels ((keyblank-size-x ()
,keyblank-size-x))
(macrolet ((with-keyblank-size-x (size-x &body body)
`(let ((,',keyblank-size-x ,size-x))
,@body)))
,@body)))))
CL-USER>
(with-keylang
(print (keyblank-size-x)))
42
42
All is well so far.
CL-USER>
(with-keylang
(with-keyblank-size-x 24
(print (keyblank-size-x))))
;Compiler warnings :
; In an anonymous lambda form: Unused lexical variable #:G123171
42
42
There's the problem. I want the symbol representing keyblank-size-x to be shadowed with the value 24, and this is not happening.
I have a feeling that the ,',
backquote pattern isn't proper for this case, as this quotes the symbol representing keyblank-size-x, and is therefore not eq. But if I try ,,
, it doesn't work, and I get this interesting compiler error:
While compiling WITH-KEYBLANK-SIZE-X :
Illegal reference to lexically defined variable #:G123192.
[Condition of type CCL::COMPILE-TIME-PROGRAM-ERROR]
EDIT:
The keyblank-size-x variable was lexically scoped, and I wanted dynamic scope for this particular case. So here's the rewrite declaring the keyblank-size-x variable to have dynamic scope:
(defmacro with-keylang (&body body)
(let ((keyblank-size-x (gensym)))
`(let ((,keyblank-size-x 42))
(declare (special ,keyblank-size-x))
(labels ((keyblank-size-x ()
,keyblank-size-x))
(macrolet ((with-keyblank-size-x (size-x &body body)
`(let ((,',keyblank-size-x ,size-x))
(declare (special ,',keyblank-size-x))
,@body)))
,@body)))))
And the test code:
CL-USER>
(with-keylang
(with-keyblank-size-x 25
(with-keyblank-size-x 21
(print (keyblank-size-x)))
(print (keyblank-size-x)))
(print (keyblank-size-x)))
21
25
42
42
Upvotes: 2
Views: 306
Reputation: 139261
If we fully expand the code (here using the Walk command in LispWorks):
(LET ((#:G19508 42))
(LABELS ((KEYBLANK-SIZE-X () #:G19508))
(MACROLET ((WITH-KEYBLANK-SIZE-X (SIZE-X &BODY BODY)
`(LET ((#:G19508 ,SIZE-X))
,@BODY)))
(LET ((#:G19508 24))
(PRINT (KEYBLANK-SIZE-X))))))
Rebinding #:G19508
has no effect and can't have. The function keyblank-size-x
has a different lexical binding. This is just the usual effect of lexical binding.
Upvotes: 4