setq
assigns a value to a variable, whereas let
introduces new variables/bindings. E.g., look what happens in
(let ((x 3))
(print x) ; a
(let ((x 89))
(print x) ; b
(setq x 73)
(print x)) ; c
(print x)) ; d
3 ; a
89 ; b
73 ; c
3 ; d
The outer let
creates a local variable x
, and the inner let
creates another local variable shadowing the inner one. Notice that using let
to shadow the variable doesn’t affect the shadowed variable’s value; the x
in line d
is the x
introduced by the outer let
, and its value hasn’t changed. setq
only affects the variable that it is called with. This example shows setq
used with local variables, but it can also be with special variables (meaning, dynamically scoped, and usually defined with defparameter
or defvar
:
CL-USER> (defparameter *foo* 34)
*FOO*
CL-USER> (setq *foo* 93)
93
CL-USER> *foo*
93
Note that setq
doesn’t (portably) create variables, whereas let
, defvar
, defparameter
, &c. do. The behavior of setq
when called with an argument that isn’t a variable (yet) isn’t defined, and it’s up to an implementation to decide what to do. For instance, SBCL complains loudly:
CL-USER> (setq new-x 89)
; in: SETQ NEW-X
; (SETQ NEW-X 89)
;
; caught WARNING:
; undefined variable: NEW-X
;
; compilation unit finished
; Undefined variable:
; NEW-X
; caught 1 WARNING condition
89
Of course, the best ways to get a better understanding of these concepts are to read and write more Lisp code (which comes with time) and to read the entries in the HyperSpec and follow the cross references, especially the glossary entries. E.g., the short descriptions from the HyperSpec for setq
and let
include:
-
SETQ
Assigns values to variables.
-
LET
let and let* create new variable bindings and execute a series
of forms that use these bindings.
You may want to read more about variables and bindings. let
and let*
also have some special behavior with dynamic variables and special
declarations (but you probably won’t need to know about that for a while), and in certain cases (that you probably won’t need to know about for a while) when a variable isn’t actually a variable, setq
is actually equivalent to setf
. The HyperSpec has more details.
There are some not-quite duplicate questions on Stack Overflow that may, nonetheless, help in understanding the use of the various variable definition and assignment operators available in Common Lisp:
- setq and defvar in lisp
- What’s difference between defvar, defparameter, setf and setq
- Assigning variables with setf, defvar, let and scope
- In Lisp, how do I fix “Warning: Assumed Special?” (re: using
setq
on undefined variables) - Difference between let* and set? in Common Lisp