cdc@uafhcx.uucp (C. D. Covington) (10/06/89)
I have had a problem with shadowed variables being NIL in the called function as exemplified by the following code. (defvar some-var "Old Value") (defun foo () (let ((some-var "New Value")) (bar)) ) (defun bar () some-var) The variable some-var in the function bar ends up NIL if I simply load the system using this code with (make-system ...) [I am using OS 4.0] Recompiling bar explicitly with c-s-C somehow solves the problem. Is order of compilation the problem? C. David Covington Assistant Professor, Electrical Engineering University of Arkansas Fayetteville, AR 72701 INTERNET cdc@uafhcx.uark.edu (501)575-6583 campus office 575-5379 research office 575-3041 research lab
RICE@SUMEX-AIM.STANFORD.EDU (10/07/89)
Hello, >> I have had a problem with shadowed variables being >> NIL in the called function as exemplified by the >> following code. >> (defvar some-var "Old Value") >> (defun foo () >> (let ((some-var "New Value")) >> (bar)) ) >> (defun bar () >> some-var) >> The variable some-var in the function bar ends up >> NIL if I simply load the system using this code with >> (make-system ...) [I am using OS 4.0] Recompiling bar >> explicitly with c-s-C somehow solves the problem. Is >> order of compilation the problem? >> C. David Covington Assistant Professor, Electrical >> Engineering University of Arkansas Fayetteville, AR >> 72701 >> INTERNET cdc@uafhcx.uark.edu (501)575-6583 campus >> office >> 575-5379 research office 575-3041 research lab My guess here is that you don't fully understand the semantics of special variables and/or the compilation dependency that is introduced by the proclamation of a variable as special and/or defsystem/make-system I believe that what you probably have in your code is, in fact, the following: File1: (defvar some-var "Old Value") File2: (defun foo () (let ((some-var "New Value")) (bar)) ) (defun bar () some-var) ;------------------------------------------------------------------------------- ;;; Defsystem decl: (defsystem frob (:Module f1 (("File1"))) (:Module f2 (("File2"))) (:Compile-Load f1) (:Compile-Load f2) ) ;------------------------------------------------------------------------------- a) The behaviour of defvar/defconst/defparameter is to mark a variable as SPECIAL, this is done by making a side-effect on the environment (on the compilation environment if the file being compiled or the global environment if the file is being loaded or the form is being EVALed.) b) In order for any function to perceive a special proclamation, such as that performed by defvar, the variable must be special within either the global environment or the compilation environment of the file being compiled. c) If you have a file which does the following: (defun foo () (let ((some-var "New Value")) (bar)) ) (defun bar () some-var) (defvar some-var "Old Value") ;;; Note declaration after use. then the function foo will not compile in such a manner that some-var will be assumed special because the defvar for some-var has not been performed by the time that the definition for foo is compiled. Note: CL does not specify block compilation of files, compilation happens form by form with the appropriate side effects being performed on the compilation environment after each form is compiled. d) If you build an example such as the one above with the defsystem shown then this will also not achieve the desired effect because make-system always attempts to compile a file with the minimum number of files loaded into the global environment. This is good practice since it forces you to make explicit your compilation dependencies. In this case no compilation dependencies have been specified between f1 and f2 so make-system will compile File1 and File2 and will then load them. Because each file compilation starts with a fresh compilation environment, even if File1 is actually compiled before File2 this will still not solve the problem. e) If you want to use make-system to make this problem go away then you should express a compilation dependency between the two files: (defsystem frob (:Module f1 (("File1"))) (:Module f2 (("File2"))) (:Compile-Load f1) (:Compile-Load f2 (:fasload f1)) ) the same sort of thing will have to be done if you have any other forms in your code that cause compilation dependencies. Examples of these are: Defmacro / calls to the macro Defstruct / uses of the defstruct accessors and type predicate Defflavor / declatation of methods Defclass / declaration of methods Note: in the case of Defstruct the accessors will not be open coded, but the correct semantics should apply. The compiler wil usually emit a warning totell you that you have used the accessor function before it was declared. If you are running a version before release 6 then you will get an error if you attempt to compile a setf to a defstruct slot before the defstruct has been defined. It is generally a safe practice to use the :Compile-Load-Init defsystem transformation if you have macros or substs, since this transformation forces the recompilation of the files that USE macros in the event of a macro definition being changed. Thus: (defsystem frob (:Module f1 (("File1"))) (:Module f2 (("File2"))) (:Module macros (("Macros"))) (:Compile-Load macros) (:Compile-Load f1) (:Compile-Load-Init f2 (macros) (:fasload f2 macros)) ) would be a safe thing to do if file module f2 had any references to macros defined in module macros. f) You can also get around this by the use of a special declaration to tell the compiler what it cannot already figure out from the environment. Thus the File1 and File2 problem could also be solved by defining foo and bar in the following way: (defun foo () (let ((some-var "New Value")) (declare (special some-var)) (bar)) ) (defun bar () (declare (special some-var)) some-var) g) It's worth reading up on the semantics of special variables to make dead sure that you understand what they are doing, since they can be very confusing. For instance: (defparameter fred 42) (defun frib () (print fred)) (defun frob (fred) (frib)) (frob 200) -> 200 Thus, the act of using a function argument whose name is the same as special variable results in a special binding of that variable. This can be very confusing and is an excellent reason in its own right for using the convention of defining special variables with *s, i.e. (defparameter *fred* 42) so as to alert the programmer that special binding is INTENDED when a function is defined like this. Also, specials are bound within a given process. So, if you do the following: (defparameter *fred* 42) (defun bax () (let ((*fred* 200)) (break))) (bax) now, if you evaluate *fred* from the break loop it will be 200 as you expected, but if you go to a different listener and evaluate *fred* it will have the value 42. This is because the binding for bax was made within the process in which you called bax. The only binding for *fred* in the other process is the global binding of 42. Similarly, if *fred* was globally unbound [it was declareed simply by saying (defvar *fred*)] then in the other process you would get an error saying that *fred* was unbound if you tried to print it. I hope that this casts a little light on the matter. Special variables are a corner of CL in which you can get severely burned if you have an over simplistic model of the semanntics, so it really is worth reading up in them. Rice.