Reputation: 804
I've tried to reduce it to the minimal example. The code runs without an error, producing the expected output. But it gives me a warning that my first variable is undefined. It seems that the second statement of progn doesn't "see" the results of the first statement. Thanks for the help!
(I originally did not have the progn construct in the code at all, but after getting this error I added it to see if that would force execution in order -- but the error is the same.)
Here's the code:
(let ((input (open "input.lisp")))
(progn (defvar var1 (read input))
(defvar arr1 (make-array var1 :initial-contents (read input))))
(close input))
(print var1)
(print arr1)
These are the contents of the file "input.lisp":
9
(10 8 6 4 2 4 6 8 10)
And this is the output I get from sbcl after executing (load "test.lisp"):
; in: DEFVAR ARR1
; (MAKE-ARRAY VAR1 :INITIAL-CONTENTS (READ INPUT))
;
; caught WARNING:
; undefined variable: VAR1
;
; compilation unit finished
; Undefined variable:
; VAR1
; caught 1 WARNING condition
9
#(10 8 6 4 2 4 6 8 10)
T
So, it seems to me that both definition statements are executing, but the second doesn't "see" the results of the first. It still constructs the array correctly because it's filled with the given initial-contents. But why isn't var1 already defined?
Upvotes: 2
Views: 507
Reputation: 65854
See the documentation for defvar
in the Hyperspec:
If a
defvar
ordefparameter
form appears as a top level form, the compiler must recognize that the name has been proclaimedspecial
.
This implies (and it seems to be the case for SBCL) that if a defvar
appears as a non-top-level form, then the compiler need not recognize that the name has been declared. So how come your defvar
s are not being compiled as top level forms? See section 3.2.3.1, Processing of Top Level Forms (point 6) for the answer: the let
surrounding your code causes it to be compiled as non-top-level forms.
So you need to defvar
your variables at top level, and then assign them later on with setf
inside the let
.
Like this. It's also usually simpler to use with-open-file
rather than open
and close
.
(defvar var1)
(defvar arr1)
(with-open-file (input "input.lisp" :direction :input)
(setf var1 (read input))
(setf arr1 (make-array var1 :initial-contents (read input))))
(print var1)
(print arr1)
The reason that you are having this trouble is that you are placing your code at top level in the file. This is a slightly unusual thing to do: the normal Lisp coding style is to put most of your code in function definitions, and then to call those functions when you need to run them.
For example, this would be a more typical way to write this kind of code, with the initialization code in its own function.
(defvar *var1* nil "Documentation for var1.")
(defvar *arr1* nil "Documentation for arr1.")
(defun init-from-file (file)
"Read *var1* and *arr1* from file."
(with-open-file (input file :direction :input)
(setf *var1* (read input))
(setf *arr1* (make-array *var1* :initial-contents (read input)))))
(when (null *var1*) (init-from-file "input.lisp"))
Upvotes: 4