Setq and defvar in Lisp

assigncommon-lisplispvariable-declarationvariables

I see that the Practical Common Lisp uses (defvar *db* nil) for setting up a global variable. Isn't it OK to use setq for the same purpose?

What are the advantages/disadvantages of using defvar vs. setq?

Best Answer

There are several ways to introduce variables.

DEFVAR and DEFPARAMETER introduce global dynamic variables. DEFVAR optionally sets it to some value, unless it is already defined. DEFPARAMETER sets it always to the provided value. SETQ does not introduce a variable.

(defparameter *number-of-processes* 10)

(defvar *world* (make-world))     ; the world is made only once.

Notice that you likely never want to DEFVAR variables with names like x, y, stream, limit, ... Why? Because these variables then would be declared special and its difficult to undo that. The special declaration is global and all further uses of the variable would use dynamic binding.

BAD:

(defvar x 10)     ; global special variable X, naming convention violated
(defvar y 20)     ; global special variable Y, naming convention violated

(defun foo ()
  (+ x y))        ; refers to special variables X and y

(defun bar (x y)  ; OOPS!! X and Y are special variables
                  ; even though they are parameters of a function!
  (+ (foo) x y))

(bar 5 7)         ; ->   24

BETTER: Always mark special variables with * in their names!

(defvar *x* 10)     ; global special variable *X*
(defvar *y* 20)     ; global special variable *Y*

(defun foo ()
  (+ *x* *y*))      ; refers to special variables X and y

(defun bar (x y)    ; Yep! X and Y are lexical variables
  (+ (foo) x y))

(bar 5 7)           ;  ->   42

Local variables are introduced with DEFUN, LAMBDA, LET, MULTIPLE-VALUE-BIND and many others.

(defun foo (i-am-a-local-variable)
   (print i-am-a-local-variable))

(let ((i-am-also-a-local-variable 'hehe))
  (print i-am-also-a-local-variable))

Now, by default the local variables in above two forms are lexical, unless they are declared SPECIAL. Then they would be dynamic variables.

Next, there are also several forms to set a variable to new values. SET, SETQ, SETF and others. SETQ and SETF can set both lexical and special (dynamic) variables.

It is required for portable code that one sets variables that are already declared. The exact effect of setting a not declared variable is undefined by the standard.

So, if you know what your Common Lisp implementation does, you can use

(setq world (make-new-world))

in the Read-Eval-Print-Loop at the toplevel. But don't use it in your code, since the effect is not portable. Typically SETQ will set the variable. But some implementation might also declare the variable SPECIAL when it doesn't know it (CMU Common Lisp does that by default). That's almost always not what one would want. Use it for casual use if you know what you do, but not for code.

Same here:

(defun make-shiny-new-world ()
  (setq world (make-world 'shiny)))

First, such variables should be written as *world* (with the surrounding * characters), to make clear that it is a global special variable. Second, it should have been declared with DEFVAR or DEFPARAMETER before.

A typical Lisp compiler will complain that above variable is undeclared. Since global lexical variables don't exist in Common Lisp, the compiler has to generate code for a dynamic lookup. Some compiler then say, okay, we assume that this is a dynamic lookup, let's declare it to be special - since that is what we assume anyway.

Related Topic