Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Dynamic variable question

22 views
Skip to first unread message

David Sletten

unread,
May 7, 2002, 8:40:35 AM5/7/02
to
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") . . .))

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

Barry Margolin

unread,
May 7, 2002, 10:17:04 AM5/7/02
to
In article <3CD7CB69...@slytobias.com>,

David Sletten <da...@slytobias.com> wrote:
>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.

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.

Erik Naggum

unread,
May 7, 2002, 10:21:10 AM5/7/02
to
* David Sletten

| 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.

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.

Paul Foley

unread,
May 8, 2002, 1:32:59 AM5/8/02
to
On Tue, 07 May 2002 12:40:35 GMT, David Sletten wrote:

> 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>"))

David Sletten

unread,
May 8, 2002, 3:14:09 AM5/8/02
to

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

David Sletten

unread,
May 8, 2002, 3:17:03 AM5/8/02
to

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

0 new messages