So I’ve been going through the 2011 book Land of Lisp, but using SBCL instead of CLISP (CLISP is what the book recommends). I’ve encountered a few hiccups, involving changing code that uses CLISP-specific functions to their SBCL equivalents, but nothing I couldn’t handle so far… until I reached this part.
In Chapter 15, on page 326, the book instructs you to try the following code in the REPL:
(defparameter *foo* (lambda () 5))
…and then:
(funcall *FOO*)
The result, of course, is 5. But then, it has you try this more complicated example:
(defparameter *foo* (let ((x 5))
(lambda ()
x)))
…and then:
(funcall *foo*)
The book says the result of this should be 5, and indeed, that’s what happens in CLISP. But the result I get from SBCL is this:
#<HASH-TABLE :TEST EQL :COUNT 1 {100134C403}>
Wait… what?!
Now, back in chapter 9, the book had me make a hash table with (defparameter x (make-hash-table))
and then add an entry to it with (setf (gethash 'yup x) '25)
, so that’s why the global value of x is set to a hash table with one entry. But why is SBCL using the global value of x, here, when CLISP uses the lexically scoped value (5)?
This isn’t a problem for me to fix, so much as it is a difference I’m pretty sure I’ll need to understand to get through the rest of the book. I can’t quite get my head around this concept of closures and lexical scoping and whatnot, yet, and this is the section that’s supposed to explain that, but now I’m even more confused. Can somebody walk me through what is happening, here?
So I’ve been going through the 2011 book Land of Lisp, but using SBCL instead of CLISP (CLISP is what the book recommends). I’ve encountered a few hiccups, involving changing code that uses CLISP-specific functions to their SBCL equivalents, but nothing I couldn’t handle so far… until I reached this part.
In Chapter 15, on page 326, the book instructs you to try the following code in the REPL:
(defparameter *foo* (lambda () 5))
…and then:
(funcall *FOO*)
The result, of course, is 5. But then, it has you try this more complicated example:
(defparameter *foo* (let ((x 5))
(lambda ()
x)))
…and then:
(funcall *foo*)
The book says the result of this should be 5, and indeed, that’s what happens in CLISP. But the result I get from SBCL is this:
#<HASH-TABLE :TEST EQL :COUNT 1 {100134C403}>
Wait… what?!
Now, back in chapter 9, the book had me make a hash table with (defparameter x (make-hash-table))
and then add an entry to it with (setf (gethash 'yup x) '25)
, so that’s why the global value of x is set to a hash table with one entry. But why is SBCL using the global value of x, here, when CLISP uses the lexically scoped value (5)?
This isn’t a problem for me to fix, so much as it is a difference I’m pretty sure I’ll need to understand to get through the rest of the book. I can’t quite get my head around this concept of closures and lexical scoping and whatnot, yet, and this is the section that’s supposed to explain that, but now I’m even more confused. Can somebody walk me through what is happening, here?
Share Improve this question asked 2 days ago Tina RussellTina Russell 1553 bronze badges 3- 1 I can't reproduce this behavior. If I defvar/defparameter X to be a hash table, both implementations return a hash table. If I don't, both implementations return 5. If you are seeing something different, I suspect you are loading something differently in each environment. Also, this is the reason why there is a tradition of surrounding special variables with asterisks. Land of Lisp errs in omitting them IMO. – Xach Commented 2 days ago
- 1 Huh, I tried it in the command line and you’re right! So far I’ve been using SBCL via Sly in Emacs, but I’ve been trying it directly in the REPL so I didn’t think there’d be a difference between that and doing it via the command line, hence why I thought the difference was between SBCL and CLISP. Now I might need to rewrite my question… (sigh) Oh, and Land of Lisp does typically surround special variables with asterisks… I guess it kinda messes that up in chapter 9. Oh, well. – Tina Russell Commented 2 days ago
- I don't think they expected you to do the exercises in Ch 15 in the same Lisp environment that you used in Ch 9. But you should see the same results in both CL versions if you do the same steps. – Barmar Commented 2 days ago
1 Answer
Reset to default 3The variable *foo*
is defined as a special variable.
CL-USER 1 > (defparameter *foo* (lambda () 5))
*FOO*
CL-USER 2 > (funcall *FOO*)
5
The variable x
is defined as a special variable.
CL-USER 3 > (defparameter x (make-hash-table))
X
CL-USER 4 > (setf (gethash 'yup x) '25)
25
The LET
expression binds the special variable x
to 5 and returns a closure with no arguments. The closure returns the value of the special variable x.
CL-USER 5 > (defparameter foo (let ((x 5))
(lambda ()
x)))
FOO
The closure gets called. x
looks up the current dynamic binding (not the lexical binding, since x
was defined to be a special variable).
The current dynamic binding for x
is the global value, which is the hash-table.
CL-USER 6 > (funcall *foo*)
#<EQL Hash Table{1} 801005C74B>