I ran across something like this:
(with-open-file (out "pung.dat" :direction :output)
(let ((*standard-output* out))
(format t "foo") . . .))
At first I couldn't figure out how it worked since I thought the LET
form created a lexical variable that FORMAT wouldn't be able to see.
After some research and experimentation I now realize that since
*STANDARD-OUTPUT* is a special variable at top level it retains this
status inside the LET form. LET does not create a lexical variable here.
Then FORMAT has access to this temporary binding and the redirection of
output works.
In my understanding of lexical/dynamic scope I recognized this distinction:
(defun foo (x)
(pung))
(defun pung ()
(1+ x))
(foo 2) => ERROR
Whereas:
(defun foo (x)
(declare (special x))
(pung))
(foo 2) => 3
What I didn't realize is that the following causes the same result as
the 2nd case above:
(defvar x 9)
(defun foo (x)
(pung))
(foo 2) => 3
The function FOO doesn't need to declare its parameter special since
DEFVAR creates a special variable X. (CLHS illustrates a similar example
on the DEFVAR/DEFPARAMETER page.)
I've always seen lexical scope described such that a variable is only
visible within the text of the form in which it's defined (e.g., LET,
DEFUN). This implies that the scope of a variable should be clear simply
by looking at the code. If I understand things correctly (now!) this is
wrong. The existence of a dynamic variable in the environment in which a
function is defined can affect the scope of said function's parameters
and local variables.
Do I have things worked out now? If so it seems kind of dangerous that
you can't tell the scope of a function's parameters in isolation. I
guess this is one reason to follow the *var* convention Kent recently
mentioned. X would be a parameter, while *X* would be the top level
special variable.
I appreciate any clarification of these issues.
Thanks,
David Sletten
That's why they're called "special" -- they violate the rule of referential
transparency.
--
Barry Margolin, bar...@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
There is a difference between a global (pervasive) and a local special
declaration that you may have missed. The former is obtained with
declaim, the latter with declare. defvar does the declaim part.
| In my understanding of lexical/dynamic scope I recognized this
| distinction:
| (defun foo (x)
| (pung))
|
| (defun pung ()
| (1+ x))
|
| (foo 2) => ERROR
|
| Whereas:
| (defun foo (x)
| (declare (special x))
| (pung))
|
| (foo 2) => 3
You should _really_ (declare (special x)) in pung, too.
| I've always seen lexical scope described such that a variable is only
| visible within the text of the form in which it's defined (e.g., LET,
| DEFUN). This implies that the scope of a variable should be clear simply
| by looking at the code. If I understand things correctly (now!) this is
| wrong. The existence of a dynamic variable in the environment in which a
| function is defined can affect the scope of said function's parameters
| and local variables.
What does it is the special proclamation/declamation, not the variable.
The two are strictly speaking separate concepts. You may pervasively
declare any symbol to have special binding independent of its top-level
binding. In other words, (defvar x) only makes it special, but it is
still unbound. You may actually use a symbol for its variable property
without making it special.
| Do I have things worked out now?
Well, no, the underlying mechanism is simpler than you think it is.
| If so it seems kind of dangerous that you can't tell the scope of a
| function's parameters in isolation.
Well, there are dangers in life. Your task is to cope with them. There
are also declared constants.
| I guess this is one reason to follow the *var* convention Kent recently
| mentioned. X would be a parameter, while *X* would be the top level
| special variable.
Generally speaking, it is wise to use the *var* convention for all
special bindings, not just global, special variables.
--
In a fight against something, the fight has value, victory has none.
In a fight for something, the fight is a loss, victory merely relief.
70 percent of American adults do not understand the scientific process.
> I just fell out of my chair. I discovered a huge hole in a topic I
> thought I understood...
> I ran across something like this:
> (with-open-file (out "pung.dat" :direction :output)
> (let ((*standard-output* out))
> (format t "foo") . . .))
FWIW, you can write just
(with-open-file (*standard-output* "pung.dat" :direction :output)
...)
The extra LET serves no purpose.
--
If that makes any sense to you, you have a big problem.
-- C. Durance, Computer Science 234
(setq reply-to
(concatenate 'string "Paul Foley " "<mycroft" '(#\@) "actrix.gen.nz>"))
Erik Naggum wrote:
> There is a difference between a global (pervasive) and a local special
> declaration that you may have missed. The former is obtained with
> declaim, the latter with declare. defvar does the declaim part.
ables.
.
.
.
>
> What does it is the special proclamation/declamation, not the variable.
> The two are strictly speaking separate concepts. You may pervasively
> declare any symbol to have special binding independent of its top-level
> binding. In other words, (defvar x) only makes it special, but it is
> still unbound. You may actually use a symbol for its variable property
> without making it special.
>
Thanks for the feedback, Erik. You've pointed out a number of things I
need to study more closely.
David Sletten
Paul Foley wrote:
>>I ran across something like this:
>>(with-open-file (out "pung.dat" :direction :output)
>> (let ((*standard-output* out))
>> (format t "foo") . . .))
>>
>
> FWIW, you can write just
>
> (with-open-file (*standard-output* "pung.dat" :direction :output)
> ...)
>
> The extra LET serves no purpose.
>
>
In general I completely agree with you. However, I can imagine a
(perhaps unusual) situation where you might want to 'release' the
binding of *STANDARD-OUTPUT* yet still maintain the stream OUT for
further output. That would be the only reason to do it the way I
illustrated.
Thanks for pointing this out.
David Sletten