fsbrn@BRL.ARPA (Ferd Brundick, VLD/LTTB) (03/21/88)
Haah, I'm not entirely sure why I'm sending this message, since it's not a question or a bug report. I guess I want to share my thoughts and get some discussion started. Oh yeah, the topic is the use of defconstant, defparameter, and defvar in Xerox Common Lisp. My first exposure to CL was on a Gould running UN*X. This went along with the CL paradigm of editing a file full of functions, starting a fresh CL process, and loading the file into it. If things got really bad you could kill the process and start over. Even if everything worked perfectly you always started with the same fresh CL image. As we all know, the environment is much different on a Xerox workstation. For one thing, you are always working in the same virtual memory unless you *like* to perform a "copy vmem" every day. This has always caused me some problems when I ported a file from the Gould to the 1109, and as a result my coding habits and styles have changed. So much for background. My pet project uses a file that contains nothing but "static" or read/only variables. According to Steele I should either use defconstant or defparameter to assign the values (the original Franz version was forced to use plain old setq). I intend to do that, but on the Gould it didn't seem to make much difference. I should have made them all defconstants because they never change during a run; I could always edit the file between runs. Instead I used defvar and it worked fine. When I got a copy of Lyric and downloaded my program I left all the declarations as defvars. Recently a member of my team (who is just learning CL, and on an 1109) asked me how to store variables. I automatically said "defvar" since that's what I was familiar with. Later the person complained because their program was modifying the variables (as it should) and they wanted to restart everything. I said in that case they should forget def<anything> and initialize all the variables in the actual code (perhaps in an init function). This is what I do in my large program for all the dynamic variables, most of which are simply set to NIL anyway. I got curious enough to start poking around with the 3 defs to see how they behaved in the Xerox environment. Steele gives guidelines for their use, but remember, he is assuming a fresh vmem every time you run a program. I created a constant, parameter, and var in the CL executive (not XCL), saved them with MAKEFILE, and performed the same series of actions on each one. First I tried to change it with setq, setf, the original def command again, and SEdit, then I reloaded the file. Some of the results were surprising, but after some thought it all made sense. For those of you who haven't tried this, here are my results. Note: when a comment says "verbosely" it means the exec displayed "New VARIABLES definition for xxx", while "silently" means no warning was made about the change. DEFCONSTANT 1. defconstant from exec. 2. setq from exec failed -- as expected 3. setf from exec failed -- as expected 4. defconstant from exec failed -- big surprise, possible bug 5. modified with SEdit and it worked [verbosely] 6. reloaded file and constant changed [verbosely] DEFPARAMETER 1. defparameter from exec. 2. setq from exec worked [silently] -- as expected 3. setf from exec worked [silently] -- as expected 4. defconstant from exec worked [verbosely] -- inconsistent with above 5. modified with SEdit and it worked [verbosely] 6. reloaded file and parameter changed [verbosely] DEFVAR 1. defvar from exec. 2. setq from exec worked [silently] -- as expected 3. setf from exec worked [silently] -- as expected 4. defvar from exec claimed it worked, but eval'ing gave old value 5. SEdit showed new value! claimed it worked, but eval'ing gave first value. 6. reloaded file and it claimed parameter changed, but it didn't 7. ran makunbound and reloaded file; the file's value was used What does all this mean? defconstant and defparameter worked the way Steele said they should (except for defconstant.4). defvar, on the other hand, required quite a bit of head scratching. What it did was "correct" but very confusing. If the atom in question is unbound, it assigns an initial value. If it already has a value, setq (and setf) change the current value but don't bother with the defvar, so nothing happens when you run FILES?. A second defvar and SEdit both do the same thing (making defconstant.4 look more like a bug), but they affect the *next* initial value, not the current value. This means if you use SEdit to change a defvar you might think it worked. FILES? will know the variable has been changed and the new defvar value will be stored when you MAKEFILE, but the variable will not be changed until the next time you load the file *after* running makunbound or by starting with a fresh vmem. Here's the discussion part. Why bother to use defvar in XCL? In my project I'm going to use nothing but defconstant and defparameter in my static variable file; for example, the atom *does.not.pattern* will use defconstant because it is a fundamental part of the program, while the version number will be a defparameter because it is frequently changing. Truly dynamic variables will be locally bound and setq'd as required because they must be reinitialized every time the program (or a subportion of it) is run. I've heard defvar is similar to Interlisp INITVARS, and defparameter is like VARS. I haven't done much with filecoms. I guess you should use defvar only in situations where you would use INITVARS, defconstant when the value changes once a year (or never), and defparameter the rest of the time. If a variable changes during a run it should be initialized by executable code; I think it sounds crude to reload a file just to change some variables. I realize that the def constructs apply to global variables. I've gone back to my habit of binding variables at the lowest possible level, declaring them "special", and accessing them through the stack instead of creating a swamp full of global variables. Again, this is much more of a problem in the Xerox environment than on a mainframe that trashes your workspace when you exit from your Lisp process. Does anyone have any comments or insights? I'm sorry if this was obvious to everyone else (although I have my doubts about that), but the Release Notes have very little to say on the subject. All flames are welcome because it was only 27 degrees this morning :-). dsw, fferd Fred S. Brundick USABRL, APG, MD. <fsbrn@brl.arpa> "If it was from the zoo, it would have 'Property of the Zoo' stamped on it."
SCHMIDT@SUMEX-AIM.STANFORD.EDU (Christopher Schmidt) (03/22/88)
I don't agree with the generalization "...that the def constructs apply to global variables." DEFVAR proclaims a variable special (CLtL, p 68.). Common Lisp lacks a global variable declaration mechanism (a serious omission, in my opinion), so Xerox Lisp provides XCL:DEFGLOBALVAR analogous to DEFVAR, that proclaims variables global instead of special. (I.e. compiled code uses the GVAR opcode which is much more efficient than the FVAR opcode.) Although your letter spelled out the implications of the following inequality, I think it bears repeating that (DEFVAR FOO 23) is not equivalent to (PROCLAIM '(SPECIAL FOO)) (SETQ FOO 23) because DEFVAR sets FOO only if it is unbound. By the same token (XCL:DEFGLOBALVAR FOO 23) is not equivalent to (PROCLAIM '(XCL:GLOBAL FOO)) (SETQ FOO 23) People writing portable Common Lisp code must write some ugly conditional proclamations, I suspect, if they want good performance. I hope your letter alerts some programmers to the pitfalls of DEFVAR. [I have little experience writing portable code, personally.] --Christopher -------