Why is SBCL complaining about setf?
Asked Answered
G

2

7

In SBCL, this will assign 'bar to foo, with warnings:

* (setf foo 'bar)
; in: SETF FOO
;     (SETF FOO 'BAR)
; ==>
;   (SETQ FOO 'BAR)
; 
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::FOO
; 
; compilation unit finished
;   Undefined variable:
;     FOO
;   caught 1 WARNING condition
BAR
* 

The following is from Touretzky. I typed this into "7.29.lisp" and saved.

(setf database
’((b1 shape brick)
(b1 color green)
(b1 size small)
(b1 supported-by b2)
(b1 supported-by b3)
(b2 shape brick)
(b2 color red)
(b2 size small)
(b2 supports b1)
(b2 left-of b3)
(b3 shape brick)
(b3 color red)
(b3 size small)
(b3 supports b1)
(b3 right-of b2)
(b4 shape pyramid)
(b4 color blue)
(b4 size large)
(b4 supported-by b5)
(b5 shape cube)
(b5 color green)
(b5 size large)
(b5 supports b4)
(b6 shape brick)
(b6 color purple)
(b6 size large)))

Whereupon:

* (load "7.29.lisp")
While evaluating the form starting at line 1, column 0
  of #P"/home/redacted/7.29.lisp":

debugger invoked on a SIMPLE-ERROR in thread
#<THREAD "main thread" RUNNING {10005D05B3}>:
  odd number of args to SETF: (SETF DATABASE ’
                                    ((B1 SHAPE BRICK) (B1 COLOR GREEN)
                                     (B1 SIZE SMALL) (B1 SUPPORTED-BY B2)
                                     (B1 SUPPORTED-BY B3) (B2 SHAPE BRICK)
                                     (B2 COLOR RED) (B2 SIZE SMALL)
                                     (B2 SUPPORTS B1) (B2 LEFT-OF B3)
                                     (B3 SHAPE BRICK) (B3 COLOR RED) ...))

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [RETRY   ] Retry EVAL of current toplevel form.
  1: [CONTINUE] Ignore error and continue loading file "/home/redacted/7.29.lisp".
  2: [ABORT   ] Abort loading file "/home/redacted/7.29.lisp".
  3:            Exit debugger, returning to top level.

(SB-C::EXPLODE-SETQ (SETF DATABASE ’ ((B1 SHAPE BRICK) (B1 COLOR GREEN) (B1 SIZE SMALL) (B1 SUPPORTED-BY B2) (B1 SUPPORTED-BY B3) (B2 SHAPE BRICK) (B2 COLOR RED) (B2 SIZE SMALL) (B2 SUPPORTS B1) (B2 LEFT-OF B3) (B3 SHAPE BRICK) (B3 COLOR RED) ...)) ERROR)
0] 

What do I need to understand to make SBCL happy with these assignments?

Gastrology answered 28/4, 2020 at 23:15 Comment(1)
Does this answer your question? setq and defvar in LispSaire
S
13

that first warning seems pretty clear to me, tbh. in (setf foo 'bar), foo is not known to refer to a variable. it hasn't been locally bound as a lexical variable by a form like lambda or let, and it hasn't been globally bound as a special variable by a form like defvar or defparameter. if you want this to appear as a top-level form, you should write (defparameter *foo* 'bar) (note that common lisp convention is to surround the names of special variables in earmuffs). if you want to do this within the body of a function or for a finite scope, you should do (let ((foo 'bar)) (do-stuff-with foo)).

as to your second problem, in addition to the fact that database is unbound, you have the wrong quote character. is not an ascii single-quote, and as such, is being parsed as a symbol. the compiler sees your form as: (setf database |’| ((b1 shape green) ...)), which is not a valid setf form. to fix this, figure out how to type the character ', and/or don't copy-paste code from formatted documents into plain text files.

Sommersommers answered 29/4, 2020 at 0:20 Comment(2)
The book from which I'm learning lisp uses setf for all assignment. It was published in 1990. Are we not doing that nowadays?Gastrology
Going by this, you should probably pick up a post 1994 edition, since Common Lisp was ANSI standardized in 1994.Lemmueu
L
3

While Phoebe's answer sums it up, I'd like to put some context that I find missing.

In common lisp, unlike some other languages (like python), and like some other languages (like C), there is distinction between variable initialization and variable assignment.

Global variables are introduced using defvar, defparameter, while local variables using let, let*, lambda and the likes. However, it's not just about being global or local - see this question on dynamic vs lexical scoping.

Once, you have introduced the variables by the above means, you can use setf for the assignment.


The proper terms used in the CLHS are:

defvar, defparameter

defparameter and defvar establish name as a dynamic variable.

Reference: http://clhs.lisp.se/Body/m_defpar.htm

let, let*

let and let* create new variable bindings...

each binding is lexical unless there is a special declaration to the contrary

Reference: http://clhs.lisp.se/Body/s_let_l.htm#let

setf

setf changes the value of place to be newvalue.

Reference: http://clhs.lisp.se/Body/m_setf_.htm

Edit: Took @tfb's comment and CLHS into account.

Lemmueu answered 29/4, 2020 at 10:37 Comment(5)
I think the terms are binding and assignment, where assignment is mutation of a binding. But in fact it's more complicated than this: (defvar *foo*) does not bind *foo*, it merely declares what sort of bindings *foo* will have (namely special aka dynamic bindings).Gormandize
It may be worth mentioning that using setf before introducing a variable with defvar or defparameter does not have defined behavior in the Standard. SBCL is rather strict, but some implementations will respond as expected (though likely will emit warnings). It is not uncommon to take advantage of this implementation-specific behavior in the REPL when playing around, and this is probably why books often show such in REPL interactions; but actual code should not use setf unprepared.Saire
@tfb I think (defvar *foo*) will both declare foo special and bind it to nil.Erving
@Vatine: did you try typing it at a CL listener, or reading the spec? Because no, it won't do that.Gormandize
@tfb No, that was from (obviosuly faulty) memory.Erving

© 2022 - 2024 — McMap. All rights reserved.