Variables: Global, Local and Special

6.1 Global Variables and Constants

defvar and defparameter create special (dynamically scoped) variables. Hence the *earmuffs* naming convention.

(defvar *counter* 0)              ; defines only if not already defined (keeps the value on reload)
(defparameter *config* "dev")     ; always (re)assigns on load
(defconstant +max-size+ 100)      ; constant (don't redefine with a different value)
(setf *counter* (1+ *counter*))   ; assignment
(incf *counter*)                  ; increment (also decf)
(incf *counter* 10)

6.2 Local Variables

(let ((x 1)
      (y 2))                      ; parallel binding
  (+ x y))                        ; 3

(let* ((x 1)
       (y (+ x 1)))               ; sequential binding
  (* x y))                        ; 2

(let ((x 10))
  (setf x 20)                     ; assign a local
  x)                              ; 20

6.3 setf: Generalized Assignment

setf works on any place (variable, car, aref, gethash, slot accessors, ...), and you can define your own places with defsetf or (defun (setf my-accessor) (new-value obj) ...).

(defparameter *list* (list 1 2 3))
(setf (first *list*) 100)         ; *list* → (100 2 3)
(setf (nth 2 *list*) 300)
(defparameter *h* (make-hash-table))
(setf (gethash :a *h*) 1)
(defparameter *v* (make-array 3 :initial-element 0))
(setf (aref *v* 0) 42)
(setf a 1 b 2)                    ; multiple assignments
(rotatef a b)                     ; swap
(shiftf a b 99)                   ; shift values left
(push 0 *list*)  (pop *list*)     ; stack operations on places
(pushnew 5 *list*)                ; push if not already present

6.4 Lexical vs Dynamic (Special) Scope

Local let bindings of non-special variables are lexical (closures capture them). Bindings of special variables are dynamic (visible to called functions until the binding exits).

(defvar *level* 0)

(defun show-level () (format t "Level: ~a~%" *level*))

(defun nested ()
  (let ((*level* 1))              ; dynamically rebinds the SPECIAL variable
    (show-level)))                ; Level: 1 (sees the dynamic binding)

(nested)
(show-level)                      ; Level: 0 (binding restored)

;; Common uses: redirect output, configure behavior temporarily
(let ((*print-base* 16)) (print 255))       ; prints FF
(let ((*standard-output* (make-broadcast-stream))) (print "silenced"))