Repeatable off-by-one issue in Common Lisp's format - common-lisp

I have format's tabs ~VT behaving differently depending on whether the newline ~% is at the beginning or the end of lines, and I wanted to know why. The difference is that when the newline is at the end of lines, there seems to be an extra space in the first instance only of the tab stop. The following examples illustrate. The only difference in the examples is in the format-control string: it's "~%~A~VT= ~A" in the first example and "~A~VT= ~A~%" in the second.
EXAMPLE 1: newline at the beginning of output lines
(let ((sb (make-array 0
:element-type 'character
:adjustable t
:fill-pointer 0)))
(mapcar (lambda (line)
(format sb "~%~A~VT= ~A" line 10 42))
'(a abcd asdf foobar g november))
sb)
"
A = 42
ABCD = 42
ASDF = 42
FOOBAR = 42
G = 42
NOVEMBER = 42"
The behavior here is as expected.
EXAMPLE 2: newline at the ends of output lines
The thing to notice in this example is that the first line,
A = 42
has one more space in it than the corresponding line from example 1:
A = 42
It's a little hard to see because of the leading double-quote, and that's why I snipped this out: to help you see them better.
This is repeatable on much bigger examples and is an MVE stripped out of a much larger program.
(let ((sb (make-array 0
:element-type 'character
:adjustable t
:fill-pointer 0)))
(mapcar (lambda (line)
(format sb "~A~VT= ~A~%" line 10 42))
'(a abcd asdf foobar g november))
sb)
"A = 42
ABCD = 42
ASDF = 42
FOOBAR = 42
G = 42
NOVEMBER = 42
"
The big-picture question is "why?" I'm using SBCL 1.3.1 on a Mac and haven't tried it on other implementations. It could be a bug, but it seems more plausible that it's intended behavior, but I don't understand what it could be intended to accomplish and I haven't been able to find an explanation in format's documentation.

I think that it is a bug. I can also reproduce it on Linux with SBCL 1.3.1.
~T may need heuristics (that can fail) to determine the current column in some cases, but I guess the start of the string should be considered column 0.
At least on my computer, it seems not to occur when a simple with-output-to-string is used:
(with-output-to-string (s)
(mapcar (lambda (line)
(format s "~A~VT= ~A~%" line 10 42))
'(a abcd asdf foobar g november)))
It does occur, however, when you give the pre-made string to with-output-to-string:
(let ((sb (make-array 0
:element-type 'character
:adjustable t
:fill-pointer 0)))
(with-output-to-string (s sb)
(mapcar (lambda (line)
(format s "~A~VT= ~A~%" line 10 42))
'(a abcd asdf foobar g november))
sb))

Related

How to step in SBCL like this?

I am new to Common Lisp and I am using SBCL, Slime and Emacs to learn.
While reading the book Common Lisp: A Gentle Introduction to Symbolic Computation, the author mentions the STEP tool which is helpful for debugging and able to do this:
It is not 100% clear if the italic text is coming from the author or the tool itself. Probably, just the author's comments.
However, even if I do not consider the italic, I am unable to generate descriptive info like this.
If I use just SBCL's REPL, I get:
* (step (if (oddp 5) 'yes 'no))
YES
If I use the REPL inside Emacs with Slime on, I get:
CL-USER> (step (if (oddp 5) 'yes 'no))
YES
The author says that:
Each implementation of Common Lisp provides its own version of this
tool; only the name has been standardized.
If I try the same thing in Emacs/Slime with a function, I get more info:
(defun my-abs (x)
(cond ((> x 0) x)
((< x 0) (- x))
(t 0)))
Using the definition above and the command bellow on the REPL:
CL-USER> (step (my-abs 10))
I get:
Evaluating call:
(MY-ABS 10)
With arguments:
10
[Condition of type STEP-FORM-CONDITION]
Restarts:
0: [STEP-CONTINUE] Resume normal execution
1: [STEP-OUT] Resume stepping after returning from this function
2: [STEP-NEXT] Step over call
3: [STEP-INTO] Step into call
4: [RETRY] Retry SLIME REPL evaluation request.
5: [*ABORT] Return to SLIME's top level.
--more--
Backtrace:
0: ((LAMBDA ()))
1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LET ((SB-IMPL::*STEP-OUT* :MAYBE)) (UNWIND-PROTECT (SB-IMPL::WITH-STEPPING-ENABLED #))) #S(SB-KERNEL:LEXENV :FUNS NIL :VARS NIL :BLOCKS NIL :TAGS NIL :TYPE-RESTRICTIONS ..
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (STEP (MY-ABS 10)) #<NULL-LEXENV>)
3: (EVAL (STEP (MY-ABS 10)))
--more--
Unfortunately, none of those options seem to give me what I want (which could be an error of interpretation on my side).
I would like to see something like:
SLIME seems to be a thorough tool. I might be missing something.
Is there a way to generate the same output described by the book using SLIME or SBCL?
As someone suggested above, SBCL does optimize away a lot by default, and compiles by default as well. Here's what I did to create an example:
I first made up "a bad value" for the "suggested optimization" by running
(declaim (optimize (debug 3) (space 0) (speed 0)))
Then, I defined a function with something more than an if condition, since that sort of thing is always inlined, unfortunately (though you might try (declaim (notinline ...)), I haven't. One way is to create a function that calls another one, like:
(defun foo () "hey!")
(defun bar () (foo))
Now, when I run (step (bar)), I see the debugger pane that you shared in your question above, and if I now select option #3, step into, I get the same pane but now focussed, as hopefully expected, on the call to foo.
Good luck!
Some references:
SBCL manual on "Single stepping": http://www.sbcl.org/manual/#Single-Stepping
SBCL manual on "Debugger policy control": http://www.sbcl.org/manual/#Debugger-Policy-Control
It looks like the author is using LispWorks' stepper.
With LispWorks
Here's my step session, using :s to step the current form and all its subforms.
CL-USER 5 > (step (my-abs -5))
(MY-ABS -5) -> :s
-5 -> :s
-5
(COND ((> X 0) X) ((< X 0) (- X)) (T 0)) <=> (IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0)))
(IF (> X 0) (PROGN X) (IF (< X 0) (- X) (PROGN 0))) -> :s
(> X 0) -> :s
X -> :s
-5
0 -> :s
0
NIL
(IF (< X 0) (- X) (PROGN 0)) -> :s
(< X 0) -> :s
X -> :s
-5
0 -> :s
0
T
(- X) -> :s
X -> :s
-5
5
5
5
5
5
Help is on :?:
:?
:s Step this form and all of its subforms (optional +ve integer arg)
:st Step this form without stepping its subforms
:si Step this form without stepping its arguments if it is a function call
:su Step up out of this form without stepping its subforms
:sr Return a value to use for this form
:sq Quit from the current stepper level
:bug-form <subject> &key <filename>
Print out a bug report form, optionally to a file.
:get <variable> <command identifier>
Get a previous command (found by its number or a symbol/subform within it) and put it in a variable.
:help Produce this list.
:his &optional <n1> <n2>
List the command history, optionally the last n1 or range n1 to n2.
:redo &optional <command identifier>
Redo a previous command, found by its number or a symbol/subform within it.
:use <new> <old> &optional <command identifier>
Do variant of a previous command, replacing old symbol/subform with new symbol/subform.
For compiled code it also has a visual stepper, where you can press a red button to set a breakpoints, see the intermediate variables changing etc. It looks like this:
LispWorks is a proprietary implementation and IDE that has a free but limited version. I just wrote a review that should be merged on the Cookbook.
trace and printv
Do you know trace? printv, an external library, is a trace on steroids. They resemble the output you appreciate.
(defun factorial (n)
(if (plusp n)
(* n (factorial (1- n)))
1))
(trace factorial)
(factorial 2)
0: (FACTORIAL 3)
1: (FACTORIAL 2)
2: (FACTORIAL 1)
3: (FACTORIAL 0)
3: FACTORIAL returned 1
2: FACTORIAL returned 1
1: FACTORIAL returned 2
0: FACTORIAL returned 6
6
(untrace factorial)
printv prints the code and the returned values.
(printv:printv
(+ 2 3)
*print-case*
*package*
'symbol
(let* ((x 0) (y (1+ x)) (z (1+ y)))
(values x y z)))
;;; (+ 2 3) => 5
;;; *PRINT-CASE* => :UPCASE
;;; *PACKAGE* => #<PACKAGE "ISSR-TEST">
;;; 'SYMBOL => SYMBOL
;;; (LET* ((X 0) (Y (1+ X)) (Z (1+ Y)))
(VALUES X Y Z)) =>
[ [X=0] [Y=1] [Z=2] ]
;;; => 0, 1, 2

Memoization with a closure example from Land of Lisp

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

How can my macro take a variable instead of an unquoted list?

I'm building an alphametic solver, and I'd like to make a macro that substitutes numbers into a symbol template.
Here is a self-contained example:
(defparameter *symbol-positions* '(#\H #\T #\S #\R #\A #\U #\E #\O #\W #\N))
(defmacro word-to-number (symbols lst)
`(tonumber (list ,#(loop for symbol in symbols
when (not (eql symbol #\ ))
collect `(nth ,(position symbol *symbol-positions*) ,lst )))))
(defparameter num '(0 1 2 3 4 5 6 7 8 9))
(defparameter east '(#\ #\E #\A #\S #\T))
The following call works:
(word-to-number (#\ #\E #\A #\S #\T) num)
But this one doesn't:
(word-to-number east num) ;=> The value EAST is not of type LIST
Is there a way I can modify the macro to take a variable for the SYMBOLS parameter? ,symbols doesn't work, and neither does `(,#symbols)
When you do:
(word-to-number east num)
The macro expander gets called with the arguments being east and num. They won't be lists and number for you macro. Only for the resulting code will they be evaluated in a contex which yields values.
A macro is syntax transformation. eg.
(cond (p1 c1)
(p2 c2)
(t a))
; ==
(if p1
c1
(if p2
c2
a))
It is regardless if p1 is (< a b) or my-boolean-value. A macro just places the expressions there without having to know what a, b or my-boolean-value is.
So tell me.. How should the expansion look with (word-to-number east num)? Perhaps it shouldn't be a macro at all? eg.
(defun word-to-number (symbols lst)
(tonumber (loop :for symbol :in symbols
:when (not (eql symbol #\ ))
:collect (nth (position symbol *symbol-positions*) lst))))
UPDATE
(defmacro word-to-number (symbols lst)
`(tonumber (loop :for symbol :in ,symbols
:with clst := ,lst
:when (not (eql symbol #\ ))
:collect (nth (position symbol *symbol-positions*) clst))))
You might notice that I'm storing lst in a variable clst and I do it after evaluating symbols. The reason is that when you expect arguments to be evaluated you expect them to be evaluated in order and only once unless repeated evaluation is a feature of the macro like loop does. Eg. this should only print "Oh, happy day" once:
(word-to-number (progn (princ "Oh") east) (progn (princ ", happy day!") num))

(push x nil) VS (push x place-that-stores-the-empty-list)

Why is it not possible to push directly on a list like '(1 2 3) or NIL?
Specifically:
Why is possible to do
> (let ((some-list nil))
(push 42 some-list))
(42)
but not to do something like
(push 42 nil)
or
(push 42 '(1 2 3))
What is the reasoning behind this implementation?
With macro push the second argument needs to be a place to be modified. Here are some examples:
Lets make two variables:
(defparameter *v* (list 2 4))
(defparameter *v-copy* *v*)
Then we push 0
(push 1 *v*) ; ==> (1 2 4)
*v-copy* ; ==> (2 4) (unaltered)
; the reason is that the variable is changed, not its value
(macroexpand '(push 1 v))
; ==> (setq v (cons 1 v))
push can use other things as second argument. Lets try a cons
(push 3 (cdr *v-copy*))
*v-copy* ; ==> (2 3 4)
; since the tail of *v* is the *v-copy* *v* is changed too
*v* ; ==> (1 2 3 4)
(macroexpand-1 '(push 2 (cdr *v-copy*)))
; ==> (rplacd v (cons 2 (cdr *v-copy*)))
If your examples were valid, what should it really have done? Lets do the nil first:
(macroexpand '(push 42 nil))
; ==> (setq nil (cons 42 nil))
This treats nil just as any other variable and if this worked nil would never be the empty list again. It would have been a list with one element, 42 and a different value than (). In Common Lisp nil is a constant and cannot be mutated. I've created a lisp once where nil was a variable like any other and a small typo redefined nil making the programs behave strange with no apparent reason.
Lets try your literal quoted list.
(macroexpand '(push 42 (quote (1 2 3))))
; ==> (let ((tmp (1 2 3)))
; (funcall #'(setf quote) (cons 42 'tmp) tmp))
It doesn't seem the push macro differentiates between special form quote and those types that has set their setf function. It won't work and it doesn't make sense. Anyway in the same manner as mutating the binding nil if this changed the literal data '(1 2 3) to '(43 1 2 3) would you then expect to get (43 1 2 3) every time you evaluated (1 2 3) from there on? I imagine that would be the only true effect of mutating a constant. If this was allowed you should be allowed to redefine 4 to be 5 so that evaluating 4 or (+ 2 2) shows the result 5.

Pass a "variable name" to defvar

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.

Resources