fsbrn@BRL.ARPA (Ferd Brundick, VLD/LTTB) (03/29/88)
Haah, Last week I posted a letter about the use of defconstant, defparameter, and defvar. I've gotten a couple of responses, and the one from Chris Schmidt pointed me in a new direction. He says he doesn't agree with my generalization "... that the def constructs apply to global variables." He then says "DEFVAR proclaims a variable special (CLtL, p 68)." Sure enough, that's what it says in the middle of page 68. However, the top line of the page says "The defvar and defparameter [and defconstant] special forms are the usual means of specifying globally defined variables [and constants]." On page 69 Steele says "These constructs are normally used only as top-level forms." I'll get back to that statement later. I think the real issue is "what are 'special' variables and how are they used (and misused)?", but first another aside. The first LISP I was exposed to was Franz, which is dynamically scoped. We were given the kernel of a program, and over a period of time it evolved and grew until only a few of the original function names remained. After we bought our first Xerox Lisp machines I translated everything into Interlisp-D, and about a year ago I rewrote it again, this time in Gould CL. Like I said in my previous note, I would bind my "global" variables to a "main" function and use Franz's dynamic scoping to access these variables from the subfunctions. There are far too many variables in the program to pass them around in argument lists. When I started using CL I read that it is lexically scoped, and I got the idea that variables must be *global* or local. (When I say *global* I mean the variables are bound at the top level. "global" or "semi-global means they are bound somewhere in the calling sequence and accessed through the stack.) I also thought "special" meant *global*. In my rewrite I didn't bind the *global* variables to anything and the program worked the way it always had. Once I got a copy of Lyric I downloaded the files and got them to work with only minor changes (mostly OS-dependent). Then I learned that "special" means "dynamically scoped" so I started binding "semi-global" variables the way I did in Franz. Now I think of (DECLARE (SPECIAL VARxxx)) to mean "export" in the function that binds VARxxx and "import" in all the functions that use VARxxx. (Why didn't Steele use the words "export" and "import" if they are accurate?) If no one binds VARxxx then it is *global*, which is essentially what I had been doing. How does this affect the Xerox environment? Again referring to my earlier note, the vmem is persistant. If a program creates any *global* variables they will hang around until they are explicitly unbound (with MAKUNBOUND), the vmem is recopied, or a (LOGOUT T) is performed (the last 2 being brute force approaches). At the Lyric conversion course I attended the instructor pointed out that *global* variables are a Bad Thing in a multi-processing environment. How can you run multiple copies of a program if they all use the same *global* variables? I have modified a module (the changes were fairly minor) to use a hash table instead of property lists to store lots of data. Not only can I bind the hash table so multiple processes may be run, but I don't have to worry about the data structures being garbage collected after I'm done (there aren't any multiple references). The issue now seems to be largely a matter of style. If you think the use of *global* or "global" variables is a Bad Thing this doesn't affect you. Property lists are by nature *global*, which is why I've switched to hash tables. What is the "preferred" way (or your favorite way, I suppose) to bind and reference global variables? I used to append a program's name to each of its functions so I could keep track of them and not overwrite another program's function with the same name. While this is still done in the Library and Lispuser modules, packages are one way around the problem. However, this doesn't help global variables with multiple copies of the same program. Yes, another technique is to use property lists on a program's main window, which is what I use for *some* of my variables. I don't want the overhead of GETing and PUTing properties every time I need to see or modify a value. My two main sources for CL information are Steele's book (CLtL) and Tatar's book, _A Programmer's Guide to CL_. Chris and I have already mentioned what Steele says about the "def" macros. His description of "special" (pp 157-8) says "All variable bindings affected are made to be dynamic binding rather than the current local binding." He then gives a simple example and a very contorted one. On pp 76-7, Tatar says "Variables are lexically scoped unless they are declared special, in which case they are dynamically scoped." She then gives a couple of examples similar to Steele's simple one. Ok so far. After the examples she says (p 78) "Special variables are not often used in this form [accessing dynamic variables on the stack]. However, there is a generalization of the 'special variable' idea which is used frequently. So-called global variables in Lisp are, in fact, dynamically scoped special variables." This is her intro to the "def" macros, the "top-level variable declarations" and free variables. Now I've come full circle. Tatar says to use def* to declare global free variables, but I don't want free vars so I can run multiple copies. Is it safe to say that both Steele and Tatar are assuming a single process, static environment, and that the Xerox environment requires new rules and techniques? As an experiment I created two functions, FOO and BAR. FOO binds a variable in a LET, tries to DEFVAR it, and calls BAR. BAR tries to access the variable. The results I got when I ran this are so weird I'm sure that I tried to use defvar in a nonsensical manner. *IF* what I did makes no sense, then it is not strictly true that def* means the same as "special". It also makes me wonder if you can defvar a variable anywhere *except* at the top level, even though Steele claims "These constructs are normally used only as top-level forms." Do I have a gross misunderstanding of how these things are "supposed" to work, or am I just making an issue out of nothing? I haven't read all of Tatar's book (yet) and I haven't seen any "real" examples of how and when to use defvar vs. dynamically scoped semi-global variables. Is Tatar wrong when she says "special" variables are rarely used? I looked at her programs near the back of the book and she prefers to use defvar at the top level for all her *global* variables and never says (DECLARE (SPECIAL VARxxx)). [When I try to compile functions that reference free variables the compiler warns me that it is assuming the variable is special. Putting in a DECLARE SPECIAL statement silences the compiler. Running interpreted doesn't seem to care. This happened on all 3 CLs I tried.] The book _Common LISPCraft_ doesn't even talk about special variables or def*. (In my opinion the book is a minor rewrite of _LISPCraft_ using CL function names instead of Franz names. A lot of CL's power is ignored.) Here is a dribble file of the test I tried under Lyric. It shows the functions FOO and BAR, 2 sample calls to FOO, a modification of FOO, and another call to FOO. What is supposed to happen is FOO binds Y to itself, makes it "special" with defvar, then calls BAR which should see the binding of Y on the stack. That happens in the second and third calls but not the first. I try to get the value of Y at the end to show that it isn't just a *global* value. 263> pp foo FUNCTIONS definition for FOO: (DEFUN FOO (X) (LET (Y) (DEFVAR Y '(INITIAL VALUE)) (FORMAT T "foo: ~A ~A~%" X Y) (BAR X))) 264> pp bar FUNCTIONS definition for BAR: (DEFUN BAR (X2) (FORMAT T "foo: ~A ~A~%" X2 Y)) 265> foo(alpha) foo: ALPHA (INITIAL VALUE) Unbound variable: Y. 266> foo(beta) foo: BETA NIL foo: BETA NIL NIL 267> ed(foo) Editing FUNCTIONS definition of FOO FOO 268> pp foo FUNCTIONS definition for FOO: (DEFUN FOO (X) (LET (Y) (DEFVAR Y '(INITIAL VALUE)) (SETQ Y '(NEW VALUE)) (FORMAT T "foo: ~A ~A~%" X Y) (BAR X))) 269> foo(gamma) foo: GAMMA (NEW VALUE) foo: GAMMA (NEW VALUE) NIL 270> y Unbound variable: Y. dsw, fferd Fred S. Brundick USABRL, APG, MD. <fsbrn@brl.arpa>