So, I have read from
setq and defvar in lisp,
http://www.cs.ucf.edu/courses/cop4020/spr2006/plsetup.html, and
In Lisp, how do I fix "Warning: Assumed Special?"
among other places about the difference between setf and defvar. So I decided to play around with the idea a bit:
CL-USER> (defun foo ()
(setf x 10)
(print x))
; in: DEFUN FOO
; (SETF X 10)
; ==>
; (SETQ X 10)
;
; caught WARNING:
; undefined variable: X
;
; compilation unit finished
; Undefined variable:
; X
; caught 1 WARNING condition
FOO
CL-USER> x
; Evaluation aborted on #<UNBOUND-VARIABLE X {10040F1543}>.
CL-USER> (foo)
10
10
CL-USER> x
10
Okay, I know that setf should be used to change the value of an existing variable, but the undefined variable warning seems to be handled pretty well in SBCL (though I have read that different CL implementations may handle this differently, thus it isn't the best thing to do).
Enter the second test:
CL-USER> (defun bar ()
(defvar y 15)
(print y))
; in: DEFUN BAR
; (PRINT Y)
;
; caught WARNING:
; undefined variable: Y
;
; compilation unit finished
; Undefined variable:
; Y
; caught 1 WARNING condition
BAR
CL-USER> y
; Evaluation aborted on #<UNBOUND-VARIABLE Y {10045033D3}>.
CL-USER> (bar)
15
15
CL-USER> y
15
As per the links, I changed the setf to defvar which I think should create and bind the variable all at once. Now my undefined variable warning gets pushed into the (print y) line ... what is going on here?
As a secondary question, I am expecting the values of any variables assinged within a funciton to be inaccessible outside of the function, as is the case in Python:
>>> def foo():
... x = 10
... print x
...
>>> foo()
10
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
I am guessing this has something to do with the way common lisp deals with scope, ie defvar creates a "global special variabe" ... So I tried one last time with (let ...)
CL-USER> (defun baz () (let ((z 10)) (print z)) (incf z 10) (print z))
; in: DEFUN BAZ
; (INCF Z 10)
; --> LET*
; ==>
; (SETQ Z #:NEW0)
;
; caught WARNING:
; undefined variable: Z
;
; compilation unit finished
; Undefined variable:
; Z
; caught 1 WARNING condition
And after reading What's difference between defvar, defparameter, setf and setq, this one seems to work right:
CL-USER> (defun apple ()
(defparameter x 10)
(print 10))
APPLE
CL-USER> x
; Evaluation aborted on #<UNBOUND-VARIABLE X {1004436993}>.
CL-USER> (apple)
10
10
CL-USER> x
10
Just to reiterate my questions:
1) what is really going on with setf, defvar and let?
2) is there a way to get common lisp to scope the variables inside a function as in the python example?
answering 2)
DEFVAR
defines a variable. But it has not been executed. So the compiler does not know about the variable in theprint
form - when compiling theDEFUN
form.. It's also inside aDEFUN
. Thus it is not on the top-level. As a top-level form the compiler would recognize theDEFVAR
and would notice thaty
is a global special variable.1)
SETF
sets a variable value, but does not define it. If that variable is undefined, then the Common Lisp standard does not really say what happens. Most Common Lisp implementations will do something useful. Typically it gets executed as if the variable would have been declared special (thus you also get a warning).DEFVAR
is used as a top-level form (usually not inside functions) to define global special variables. SinceDEFVAR
declares the variable name to be special, it is a very useful convention to write a variable with stars around it:*y*
instead of justy
.LET
defines a scope for local variables.2) Common Lisp functions have the parameter list to introduce variables. Other than that they don't define a variable scope. If you want to introduce a local variable inside a function, use
LET
.Is
Again: a function does not provide a scope for variables, such that assigning a variable inside a function will automagically define it to be function-local. Use
LET
instead.Note also that
LET
is syntactic sugar, mostly:(let ((a 1) (b 2)) (+ a b))
is basically doing the same as((lambda (a b) (+ a b)) 1 2)
. It's just a simple function application written differently to improve it for the human reader.There is also support in Common Lisp for an older syntax:
Above defines a local variable
X
, just like aLET
would do.I'll keep it short and simple:
defvar
anddefparameter
are used to declare and set globally special variables. They only differ when the name is already bound.setf
is used for assignment (to special variables, lexical variables, and other – possibly custom – setfable places.)let
(andlet*
) creates new variable bindings that are visible inside its body. It may create either lexical or special bindings, depending on (global or local) declarations. If there are no special declarations, lexical bindings will be created.Put the code inside
let
's body, where the binding is visible: