In my experiments below I've abbreviated where the REPL returns an error, & added [num] so these can be referenced in discussion.
I'm a bit confused as to why my attempts to call a function stored in a variable are failing. It seems to me that the syntax is more complex than it needs to be.
Why can I issue neither (f 3) nor even (#'f 3)?
Is sharp quote not allowed as the first element of a form?
Why is funcall required here?
[235]> (setf f #'abs) ; I'm ok with this
#<SYSTEM-FUNCTION ABS>
[236]> (abs 3) ; This is fine
3
[237]> (f 3) ; Err due to sep. fn namespace. OK.
-- Err[1]: "Undefined function f" --
[238]> (#'f 3) ; Don't get what this err is telling me...
-- Err[2]: "#'F is not a function name, try using a symbol instead"
[239]> (funcall #'f 3) ; seems very long winded...!
3
Does this mean system functions are treated differently from user defined functions?
For completeness:
[240]> (funcall abs 3)
-- Err[3]: variable ABS has no value -- ; I get why this is an error.
[241]> (funcall #'abs 3) ; Isn't this verbose... ?
3
I haven't got to the symbols chapter in ANSI Common Lisp yet, maybe that will help... thanks for any tips.
[235]> (setf f #'abs) ; I'm ok with this
#<SYSTEM-FUNCTION ABS>
Above kind of sets a variable named f to a function object - from the function called abs.
[236]> (abs 3) ; This is fine
3
Above called the function abs.
[237]> (f 3) ; Err due to sep. fn namespace. OK.
Above: there is no function named f.
-- Err[1]: "Undefined function f" --
[238]> (#'f 3) ; Don't get what this err is telling me...
-- Err[2]: "#'F is not a function name, try using a symbol instead"
Above: Common Lisp accepts only symbols as function names, symbols as macro names, symbols as special operators or lambda expressions as the first element of a cons form. (function f) is not a function name.
Does this mean system functions are treated differently from user defined functions?
No.
[239]> (funcall #'f 3) ; seems very long winded...!
3
Above calls the function funcall with the function object from the named function f. funcall then calls this function object with 3 as the argument.
seems very long winded
It is.
Why can I issue neither (f 3) nor even (#'f 3)? Is sharp quote not allowed as the first element of a form?
Because f is not naming a function. It names a variable. #'f is also not a function name. We are required to use a function name (a symbol actually).
Namespaces
Common Lisp (like some other Lisp dialects) has two namespaces for functions and for variables.
Defining a variable foo:
CL-USER 54 > (defvar foo 3)
FOO
Defining a function foo:
CL-USER 55 > (defun foo (x) (* foo 10))
FOO
We can call the function foo with the value obtained from the variable foo:
CL-USER 56 > (foo foo)
30
How to get the function object from the global name of the function:
CL-USER 57 > (fdefinition 'foo)
#<interpreted function FOO 4060001CAC>
CL-USER 58 > (symbol-function 'foo)
#<interpreted function FOO 4060001CAC>
Same as above, but with a short notation:
CL-USER 58a > #'foo
#<interpreted function FOO 4060001CAC>
CL-USER 59 > (function foo) ; works also for local functions
#<interpreted function FOO 4230008AAC>
How to get a value from a global variable:
CL-USER 60 > (symbol-value 'foo)
3
Or just use the variable:
CL-USER 61 > foo
3
Some positives:
Positive: No name clashes.
We can write
(defun foo (list) (list list))
and don't have to write
(defun foo (lst) (list lst))
Positive: simpler compilation
(let ((list 3))
(list 1 list 3))
Above will never be an error in Common Lisp. In Scheme it would be an error: 3 is not a function.
Related
In my experiments below I've abbreviated where the REPL returns an error, & added [num] so these can be referenced in discussion.
I'm a bit confused as to why my attempts to call a function stored in a variable are failing. It seems to me that the syntax is more complex than it needs to be.
Why can I issue neither (f 3) nor even (#'f 3)?
Is sharp quote not allowed as the first element of a form?
Why is funcall required here?
[235]> (setf f #'abs) ; I'm ok with this
#<SYSTEM-FUNCTION ABS>
[236]> (abs 3) ; This is fine
3
[237]> (f 3) ; Err due to sep. fn namespace. OK.
-- Err[1]: "Undefined function f" --
[238]> (#'f 3) ; Don't get what this err is telling me...
-- Err[2]: "#'F is not a function name, try using a symbol instead"
[239]> (funcall #'f 3) ; seems very long winded...!
3
Does this mean system functions are treated differently from user defined functions?
For completeness:
[240]> (funcall abs 3)
-- Err[3]: variable ABS has no value -- ; I get why this is an error.
[241]> (funcall #'abs 3) ; Isn't this verbose... ?
3
I haven't got to the symbols chapter in ANSI Common Lisp yet, maybe that will help... thanks for any tips.
[235]> (setf f #'abs) ; I'm ok with this
#<SYSTEM-FUNCTION ABS>
Above kind of sets a variable named f to a function object - from the function called abs.
[236]> (abs 3) ; This is fine
3
Above called the function abs.
[237]> (f 3) ; Err due to sep. fn namespace. OK.
Above: there is no function named f.
-- Err[1]: "Undefined function f" --
[238]> (#'f 3) ; Don't get what this err is telling me...
-- Err[2]: "#'F is not a function name, try using a symbol instead"
Above: Common Lisp accepts only symbols as function names, symbols as macro names, symbols as special operators or lambda expressions as the first element of a cons form. (function f) is not a function name.
Does this mean system functions are treated differently from user defined functions?
No.
[239]> (funcall #'f 3) ; seems very long winded...!
3
Above calls the function funcall with the function object from the named function f. funcall then calls this function object with 3 as the argument.
seems very long winded
It is.
Why can I issue neither (f 3) nor even (#'f 3)? Is sharp quote not allowed as the first element of a form?
Because f is not naming a function. It names a variable. #'f is also not a function name. We are required to use a function name (a symbol actually).
Namespaces
Common Lisp (like some other Lisp dialects) has two namespaces for functions and for variables.
Defining a variable foo:
CL-USER 54 > (defvar foo 3)
FOO
Defining a function foo:
CL-USER 55 > (defun foo (x) (* foo 10))
FOO
We can call the function foo with the value obtained from the variable foo:
CL-USER 56 > (foo foo)
30
How to get the function object from the global name of the function:
CL-USER 57 > (fdefinition 'foo)
#<interpreted function FOO 4060001CAC>
CL-USER 58 > (symbol-function 'foo)
#<interpreted function FOO 4060001CAC>
Same as above, but with a short notation:
CL-USER 58a > #'foo
#<interpreted function FOO 4060001CAC>
CL-USER 59 > (function foo) ; works also for local functions
#<interpreted function FOO 4230008AAC>
How to get a value from a global variable:
CL-USER 60 > (symbol-value 'foo)
3
Or just use the variable:
CL-USER 61 > foo
3
Some positives:
Positive: No name clashes.
We can write
(defun foo (list) (list list))
and don't have to write
(defun foo (lst) (list lst))
Positive: simpler compilation
(let ((list 3))
(list 1 list 3))
Above will never be an error in Common Lisp. In Scheme it would be an error: 3 is not a function.
Let us suppose we have a function func1 :
(defun func1 (&rest values)
; (do something with values...)
(loop for i in values collect i))
Now, we have a function func2 which calls func1 :
(defun func2 (&rest values)
; (do something with values...)
(func1 ???))
What should I put instead of ??? to "copy" all the parameters of func2's values to func1's values ?
For instance, I would have the following behavior :
(func2 1 2 3 4) ; result is (1 2 3 4) and not ((1 2 3 4)).
In an earlier question I tried to do something like this :
(defun func2 (&rest values)
(macrolet ((my-macro (v)
`(list ,#v)))
(func1 (my-macro values))))
But the defun cannot get the value because it is not runtime. In this answer, he suggested that I use apply, but this function takes a &rest parameter too, so it doesn't solve my problem...
If possible, I would rather avoid to change the prototype of both functions, and the behavior of func1.
In common lisp, it has to be
(apply #'func1 values) ;; since `func1` has to be looked up in function namespace
remember, Clojure and Racket/Scheme are Lisp1, and common lisp is Lisp2.
Alternative solution (just for the sake)
I was asking myself, how to get it done without apply - just for the sake.
The problem with
`(func2 ,#values)
is, that if e.g.
(func2 (list 1 2 3) (list 4) 5)
is called, the values variable is ((1 2 3) (4) 5)
But when it is spliced into (func1 ,#values), what is created is
(func1 (1 2 3) (4) 5). But if we compare this with the func2 call,
it should be rather (func1 (list 1 2 3) (list 4) 5) which is perhaps not possible, because when (func2 (list 1 2 3) (list 4) 5) is called -
in the lisp manner - the arguments of func2 are each evaluated, before they enter the function body of func2, so we end up with values as a list of already evaluated arguments, namely ((1 2 3) (4) 5).
So somehow, concerning the arguments for func1 in the last expression, we are one evaluation-step offbeat.
But there is a solution with quote, that we manage to quote each of the arguments before giving it to func1 in the last expression, to "synchronize" the func1 function call - to let the arguments' evaluation pause for one round.
So my first aim was to generate a new values list inside the func2 body where each of the values list's argument is quoted (this is done in the let-binding).
And then at the end to splice this quoted-values list into the last expression: (func1 '(1 2 3) '(4) '5) which can be regarded as equivalent to (func1 (list 1 2 3) (list 4) 5) for this kind of problems / for this kind of calls.
This was achieved by this code:
(defun func2 (&rest vals)
(let ((quoted-values (loop for x in vals
collect `',x)))
; do sth with vals here - the func2 function -
(eval `(func1 ,#quoted-values))))
This is kind of a macro (it creates code btw. it organizes new code) but executed and created in run-time - not in pre-compile time. Using an eval we execute that generated code on the fly.
And like macroexpand-1, we can look at the result - the code - to which the func1 expression "expands", by removing eval around it - I call it func2-1:
(defun func2-1 (&rest vals)
(let ((quoted-values (loop for x in vals
collect `',x)))
; do sth with vals here - the func2 function -
`(func1 ,#quoted-values)))
And if we run it, it returns the last expression as code immediately before it is evluated in the func2 version:
(func2-1 (list 1 2 3) (list 4) 5)
;; (FUNC1 '(1 2 3) '(4) '5) ;; the returned code
;; the quoted arguments - like desired!
And this happens if we call it using func2 (so with evaluation of the func1 all:
(func2 (list 1 2 3) (list 4) 5)
;; ((1 2 3) (4) 5) ;; the result of (FUNC1 '(1 2 3) '(4) '5)
So I would say this is exactly what you desired!
lists vs. spread arguments
In Common Lisp it is good style to pass lists as lists and not as spread arguments:
(foo (list 1 2 3)) ; better interface
(foo 1 2 3) ; interface is not so good
The language has been defined in a way that efficient function calling can be used by a compiler and this means that the number of arguments which can be passed to a function is limited. There is a standard variable which will tell us how many arguments a particular implementation supports:
This is LispWorks on my Mac:
CL-USER 13 > call-arguments-limit
2047
Some implementations allow much larger number of arguments. But this number can be as low as 50 - for example ABCL, Common Lisp on the JVM, allows only 50 arguments.
Computing with argument lists
But sometimes we want the arguments as a list and then we can use the &rest parameter:
(lambda (&rest args)
(print args))
This is slightly in-efficient, since a list will be consed for the arguments. Usually Lisp tries to avoid to cons lists for arguments - they will be passed in registers or on the stack - if possible.
If we know that the argument list will not be used, then we can give the compiler a hint to use stack allocation - if possible:
(lambda (&rest args)
(declare (dynamic-extent args))
(reduce #'+ args))
In above function, the list of arguments can be deallocated when leaving the function - because the argument list is no longer used then.
If you want to pass these arguments to another function you can use FUNCALL and usually more useful APPLY:
(lambda (&rest args)
(funcall #'write (first args) (second args) (third args)))
or more useful:
(lambda (&rest args)
(apply #'write args))
One can also add additional arguments to APPLY before the list to apply:
CL-USER 19 > ((lambda (&rest args)
(apply #'write
(first args) ; the object
:case :downcase ; additional args
(rest args))
(values))
'(defun foo () 'bar)
:pretty t
:right-margin 15)
(defun foo ()
'bar)
There is the infix package for common lisp (see http://www.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/lisp/code/syntax/infix/infix.cl), which transforms infix form to prefix.
For example:
(string->prefix "1+2") ;; => (+ 1 2)
It also provides the reader macro #I, which can evaluate an infix form, e.g.,
#I(1+2) ;; => 3
But I don't want to use this reader macro.
I have written a simple function, which uses cl-ppcre, to substitute a string with a number, i.e.,
(prepare-form "1+x*x" "x" 3) ;; => "1+3*3"
Finally, I have a function which evaluates the infix form
(defun eval-infix (form &rest args)
(eval (string->prefix (apply #'prepare-form form args))))
Can the function eval-infix be implemented without using the eval function?
My final goal is to call eval-infix like this:
(eval-infix "1+x*x" "x" (+ 1 2))
Well I think what you want to use is string->prefix. This will intern symbols so to avoid polluting your own package lets define one. Finally you can avoid the security problems of eval by defining your own eval. There is still a problem where symbols from other packages can be written and this makes me sad. There is also the problem that the infix reader can escape to the Lisp reader which can do arbitrary evaluation. Here is a sketch of the parts of the solution. I’ve probably got the arguments for some functions in the wrong order.
(defpackage infix-vars)
(defun read-infix-string (s) (let ((*package* (find-package "INFIX-VARS"))) (string->prefix s)))
(defun substitute-var (expr v val)
(let ((v (etypecase v (symbol v) (string (intern v (find-package "INFIX-VARS"))))))
(subst expr v val)))
(defun eval-expr (e)
(etypecase e
(number e)
(list
(ecase (car e)
(+ (apply #'+ (mapcar #'eval-expr (cdr e))))))))
On page 329 of Land of Lisp, Conrad Barski explains the technique of memoization with the following example code
(let ((old-neighbors (symbol-function 'neighbors))
(previous (make-hash-table)))
(defun neighbors (pos)
(or (gethash pos previous)
(setf (gethash pos previous) (funcall old-neighbors pos)))))
The idea is nice: when I call the neighbors function, I save the result into a hash table, so that the next time calling neighbors with the same value of pos, I can just look-up the result without having to compute it again.
So I was wondering, whether it would not be easier to rename the function neighbors to old-neighbors by editing and recompiling its source code (given on page 314 of the book). Then the memoization example could be simplified to
(let ((previous (make-hash-table)))
(defun neighbors (pos)
(or (gethash pos previous)
(setf (gethash pos previous) (funcall old-neighbors pos)))))
or, by turning previous into a global variable *previous-neighbors* beforehand, even into
(defun neighbors (pos)
(or (gethash pos *previous-neighbors*)
(setf (gethash pos *previous-neighbors*) (funcall old-neighbors pos))))
thus rendering the closure unnecessary.
So my question is: what is the reason for doing it this way?
Reasons I could imagine:
It's didactical, showing what could be done with a closure (which had been explained just before) and providing an example of symbol-function.
This technique is applicable even in situations, where you cannot or may not change the source code of neighbors.
I am missing something.
You have made some good observations.
Generally the style to use closures like that is more likely to be found in Scheme code - where Scheme developers often use functions to return functions.
Generally as you have detected it makes little sense to have a memoize function foo calling an function old-foo. Using global variables reduces encapsulation (-> information hiding), but increases the ability to debug a program, since one can more easily inspect the memoized values.
A similar, but potentially more useful, pattern would be this:
(defun foo (bar)
<does some expensive computation>)
(memoize 'foo)
Where ˋmemoizeˋ would be something like this
(defun memoize (symbol)
(let ((original-function (symbol-function symbol))
(values (make-hash-table)))
(setf (symbol-function symbol)
(lambda (arg &rest args)
(or (gethash arg values)
(setf (gethash arg values)
(apply original-function arg args)))))))
The advantage is that you don't need to write the memoizing code for each function. You only need one function memoize. In this case the closure also makes sense - though you could also have a global table storing memoize tables.
Note the limitations of above: the comparison uses EQL and only the first argument of the function to memoize.
There are also more extensive tools to provide similar functionality.
See for example:
https://github.com/fare/fare-memoization
https://github.com/sharplispers/cormanlisp/blob/master/examples/memoize.lisp
https://github.com/AccelerationNet/function-cache
Using my memoize from above:
CL-USER 22 > (defun foo (n)
(sleep 3)
(expt 2 n))
FOO
CL-USER 23 > (memoize 'foo)
#<Closure 1 subfunction of MEMOIZE 40600008EC>
The first call with arg 10 runs three seconds:
CL-USER 24 > (foo 10)
1024
The second call with arg 10 runs faster:
CL-USER 25 > (foo 10)
1024
The first call with arg 2 runs three seconds:
CL-USER 26 > (foo 2)
4
The second call with arg 2 runs faster:
CL-USER 27 > (foo 2)
4
The third call with arg 10 runs fast:
CL-USER 28 > (foo 10)
1024
I've been struggling with this for two days now, and I can't find the answer.
What I want is to define three variables, a, b, and c each with a value of 0.
Naive:
(dolist (lbl '(a b c)) (defvar lbl 0))
Doesn't do what I want. a, b, and c remain undefined, and lbl now has a value of 0.
I think I may understand why this can't work: defvar is a macro, not a function, and as such I am passing it the form lbl, not the current value of label (which is a, b, c in turn). I think.
But in the resulting macroexpansion, shouldn't lbl eventually be linked-up(?) or evaluated(?) to the value I'm intending? Obviously not, either because it can't be done or I'm doing it wrong.
I want to understand:
How to make this work: (dolist (lbl '(a b c)) (defvar lbl 0))
What's going wrong under the hood. I have a feeling it has something to do with symbols or the mechanics of the quote operator.
Here are a few options:
With eval, by building a defvar expression:
(dolist (lbl '(a b c))
(eval `(defvar ,lbl 0))
With proclaim and setf of symbol-value (note: set is deprecated, since 1994 for what it's worth):
(dolist (lbl '(a b c))
(proclaim `(special ,lbl))
(setf (symbol-value lbl) 0))
This is actually mostly what defvar does (see notes in the linked page), but each Lisp implementation usually also records source file location, as they do for other defining macros.
Under the hood, defvar is a macro that makes the variable special (i.e. with dynamic extent bindings in the current dynamic environment; note: there's no portable undoing for this!), and optionally initializes it if it's not yet bound.
The fact that it's a macro means it doesn't evaluate its arguments, so it can take the variable name literally, and it does so. As such, (defvar lbl 0) will define the variable lbl, not the symbol stored in a lbl variable.
The fact that it optionally initializes the variable means that the initializing expression will not even be evaluated if the variable is boundp. So, its secondary effects won't happen if the variable is already initialized. This might or might not be expected.
Note that this expression isn't actually evaluated at macro-expansion time, it's left for evaluation when the expansion is evaluated, which in a REPL means right after macro expansion (and possibly after compilation, depending on the Lisp implementation; read more about evaluation and compilation, it's quite interesting).
Similar:
(dolist (lbl '(a b c))
(let ((lbl 0))
(print lbl)))
Why is lbl 0 and not some of a, b, c?
Because LET binds the symbol lbl and not its value.
Similar with (DEFVAR FOO 3).
Imagine following code:
(DEFVAR FOO 3)
(LET ((FOO 3)) ...)
Now, if we compile this code, the Lisp compiler recognizes the DEFVAR declaration and now knows that FOO is a special global variable. Thus in the let form FOO will be dynamically bound.
Compare this code:
(dolist (v '(FOO)) (eval `(DEFVAR ,v 3)))
(LET ((FOO 3)) ...)
The compiler won't see the DEFVAR and does not know that it should be a global special variable. In the LET form, FOO will have a lexical binding.
Thus DEFVAR needs to be a macro which knows the symbol at compile time (!) and which expands into a form that informs the compiler that the symbol is a special global variable. The form also sets the value when executed.
Thus the best way to create multiple DEFVAR declarations from a list of variables is to write a macro, which expands into a PROGN form with multiple DEFVARs. Inside the PROGN, the compiler will still recognize them.
CL-USER 21 > (pprint (macroexpand '(defvar* (a b c) 0)))
(PROGN (DEFVAR A 0) (DEFVAR B 0) (DEFVAR C 0))
Implemented as:
(defmacro defvar* (vars initial-value)
`(progn
,#(loop for var in vars
do (check-type var symbol)
collect `(defvar ,var ,initial-value))))
Note that it makes sense to check that the variables are really provided as symbols.
defvar is a special form which makes sure the symbol of it's first argument is a bound variable. If the variable is not bound the evaluated expression of the second argument becomes the bound variables value. Thus:
(defvar *x* 10) ; if *x* was not bound it's now 10
(defvar *x* 20) ; since *x* is defined nothing happens
Notice that *x* is not evaluated but is used unevaluated. In order to get the same functionality by using a variable that evaluates to a symbol which you want to exist as a variable in global scope you need to do something like this:
(defvar b 10)
(dolist (lbl '(a b c))
(when (not (boundp lbl))
(setf (symbol-value lbl) 0)))
Still, neither of the ones not already bound becomes special like with defvar, but at least you get the same behaviour:
(list a b c) ; => (0 10 0)
Perhaps you should just do:
(defvar *a* 0)
(defvar *b* 0)
(defvar *c* 0)
If you have a lot of variables you need to do this with you can do:
(defmacro defvars (lst value)
(loop :for e :in lst
:collect `(defvar ,e ,value) :into result
:finally (return (cons 'progn result))))
(defparameter *w* 10)
(defvars (*q* *w* *e*) 1)
(list *q* *w* *e* ; ==> (1 10 1)
Also, it's really important to earmuff your global variables. Once special it will follow dynamic binding. eg.
(defun test ()
(let ((*b* 15))
(test2)))
(defun test2 ()
*b*)
(test) ; ==> 15
Reimplementing DEFVAR
You can approximate the behavior of defvar with a function like this:
(defun %defvar (symbol value documentation)
"Define a global special variable.
symbol---a symbol
value---nil or a function of zero arguments
documentation---nil or a documentation string
returns symbol
Proclaim SYMBOL globally as a special variable. If VALUE is non-nil,
then if SYMBOL is not already bound, SYMBOL is assigned the value
returned by calling VALUE. DOCUMENATION is assigned as the
documentation of type variable to for SYMBOL."
(prog1 symbol
;; make it globally special
(proclaim (list 'special symbol))
;; if a value is provided, and symbol isn't
;; already bound, set its value to the result
;; of calling the value-function
(when (not (null value))
(unless (boundp symbol)
(setf (symbol-value symbol)
(funcall value))))
;; set the documentation
(setf (documentation symbol 'variable) documentation)))
Then you can do, e.g.,
CL-USER> (%defvar '*the-answer* (lambda () 42) "the answer")
*THE-ANSWER*
CL-USER> *the-answer*
42
CL-USER> (documentation '*the-answer* 'variable)
"the answer"
And with your original code, you could do something like:
(dolist (lbl '(a b c)) (%defvar lbl (lambda () 0)))
Now, how does this relate to what defvar actually does? Well, you could now implement a defvar like macro by doing:
(defmacro define-var (symbol &optional (value nil value-p) documentation)
`(%defvar
',symbol
,(if value-p `(lambda () ,value) 'nil)
,documentation))
This expands as we'd expect:
CL-USER> (macroexpand-1 '(define-var *the-answer* 42 "the answer"))
(%DEFVAR '*THE-ANSWER* (LAMBDA () 42) "the answer")
You can actually use macroexpand to look at what an implementation does, too. E.g., in SBCL:
CL-USER> (macroexpand-1 '(defvar *the-answer* 42 "the answer"))
(PROGN
(EVAL-WHEN (:COMPILE-TOPLEVEL) (SB-IMPL::%COMPILER-DEFVAR '*THE-ANSWER*))
(SB-IMPL::%DEFVAR '*THE-ANSWER* (UNLESS (BOUNDP '*THE-ANSWER*) 42) 'T
"the answer" 'T (SB-C:SOURCE-LOCATION)))
This isn't too much different from what we wrote above, though it's handling the non-evaluation of the form when the variable is already bound in a slightly different way, and it's also got some handling for recording a source location. The general idea is the same, though.
Why things don't get "linked up"
But in the resulting macroexpansion, shouldn't lbl eventually be
linked-up(?) or evaluated(?) to the value I'm intending?
The original code is:
(dolist (lbl '(a b c)) (defvar lbl 0))
We can macroexpand this to see what it becomes (in SBCL):
CL-USER> (macroexpand '(dolist (lbl '(a b c)) (defvar lbl 0)))
(BLOCK NIL
(LET ((#:N-LIST1022 '(A B C)))
(TAGBODY
#:START1023
(UNLESS (ENDP #:N-LIST1022)
(LET ((LBL (TRULY-THE (MEMBER C B A) (CAR #:N-LIST1022))))
(SETQ #:N-LIST1022 (CDR #:N-LIST1022))
(TAGBODY (DEFVAR LBL 0)))
(GO #:START1023))))
NIL)
T
Now, we can still see LBL in two places, including in (defvar LBL 0). So why don't things get "matched up"? To see that, we need to remember that the defvar inside the let will also be macroexpanded. To what? This:
CL-USER> (macroexpand '(DEFVAR LBL 0))
(PROGN
(EVAL-WHEN (:COMPILE-TOPLEVEL) (SB-IMPL::%COMPILER-DEFVAR 'LBL))
(SB-IMPL::%DEFVAR 'LBL (UNLESS (BOUNDP 'LBL) 0) 'T NIL 'NIL
(SB-C:SOURCE-LOCATION)))
But now we see that SBCL's internals are getting the symbol named "LBL"; the call (sb-impl::%defvar 'lbl …) is calling the function sb-impl::%defvar with the symbol lbl, and there's no connection between that symbol and the lexical variable that happens to be represented in the source by the same symbol. After all, if you write:
CL-USER> (let ((a 89))
(list 'a a))
(A 89)
You want to be able to get the symbol a and the number 89, right? The macroexpansion of defvar includes a call to a function with the quotation of one of the arguments to macro.