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

Declarations in LET*

7 views
Skip to first unread message

Edi Weitz

unread,
Oct 10, 2002, 6:27:20 AM10/10/02
to
From playing around with CMUCL, SBCL, LispWorks, AllegroCL, and CLISP
it looks to me as if BAR-1 and BAR-2 in the following example

(defun foo ()
(declare (special *a*))
*a*)

(defun bar-1 ()
(let ((*a* 37))
(declare (special *a*))
(let ((b (foo)))
b)))

(defun bar-2 ()
(let* ((*a* 37)
(b (foo)))
(declare (special *a*))
b))

behaved identically, i.e. as if in the LET* form *A* is already
special when B is bound.

However, rather than fiddling around with a finite number of
implementations I'd prefer to find the place in the standard where
this behaviour is explained. Unfortunately, I'm not sure where to look
at. The closest thing I came across was the sentence

"For LET*, a variable's scope also includes the remaining initial
value forms for subsequent variable bindings."

in <http://www.lispworks.com/reference/HyperSpec/Body/s_let_l.htm>. Is
that the key? In my layman's language I'd say that in both BAR-1 and
BAR-2 the SPECIAL declaration governs the specialness of *A* within
its scope which extends to the call of FOO in the case of BAR-2. Is
that about right?

Thanks for your help,
Edi.

Michael Hudson

unread,
Oct 10, 2002, 7:44:22 AM10/10/02
to
Edi Weitz <e...@agharta.de> writes:

> From playing around with CMUCL, SBCL, LispWorks, AllegroCL, and CLISP
> it looks to me as if BAR-1 and BAR-2 in the following example
>
> (defun foo ()
> (declare (special *a*))
> *a*)
>
> (defun bar-1 ()
> (let ((*a* 37))
> (declare (special *a*))
> (let ((b (foo)))
> b)))
>
> (defun bar-2 ()
> (let* ((*a* 37)
> (b (foo)))
> (declare (special *a*))
> b))
>
> behaved identically, i.e. as if in the LET* form *A* is already
> special when B is bound.

Well, I'm hardly possessed of a expert CLers intuition, but I'd have
been rather horrified to have found otherwise.

> However, rather than fiddling around with a finite number of
> implementations I'd prefer to find the place in the standard where
> this behaviour is explained. Unfortunately, I'm not sure where to look
> at. The closest thing I came across was the sentence
>
> "For LET*, a variable's scope also includes the remaining initial
> value forms for subsequent variable bindings."
>
> in <http://www.lispworks.com/reference/HyperSpec/Body/s_let_l.htm>. Is
> that the key? In my layman's language I'd say that in both BAR-1 and
> BAR-2 the SPECIAL declaration governs the specialness of *A* within
> its scope which extends to the call of FOO in the case of BAR-2. Is
> that about right?

I'd have thought the point was that special declarations like this one
affect the binding, i.e. *a* is special right from the get-go in
BAR-2.

Put another way, I guess rewriting let*s as nested lets isn't quite as
simple as a naive approach would suggest...

Cheers,
M.

--
/* I'd just like to take this moment to point out that C has all
the expressive power of two dixie cups and a string.
*/ -- Jamie Zawinski from the xkeycaps source

Tim Bradshaw

unread,
Oct 10, 2002, 11:46:47 AM10/10/02
to
Edi Weitz <e...@agharta.de> wrote in message news:<87ofa2k...@bird.agharta.de>...

> The closest thing I came across was the sentence
>
> "For LET*, a variable's scope also includes the remaining initial
> value forms for subsequent variable bindings."
>
> in <http://www.lispworks.com/reference/HyperSpec/Body/s_let_l.htm>. Is
> that the key? In my layman's language I'd say that in both BAR-1 and
> BAR-2 the SPECIAL declaration governs the specialness of *A* within
> its scope which extends to the call of FOO in the case of BAR-2. Is
> that about right?
>

I think that this does explain it, although I think it's not really
explicit.

One thing, it seems to me, is to consider what would happen if *A*
wasn't (yet) special at the point where subsequent initforms were
evaluated. Think about this, for instance (I'm going to use %A% as
the varname, to try and indicate that there is no overriding global
special declaration for it - I use this convention in my own code...):

(let* ((%A% 3)
(b #'(lambda () %a%)))
(declare (special %a%))
b)

If funcalling the value of this form (in a context where there is no
special binding of %a%) succeeds, then there really must be *two*
bindings of %a% - there must be a secret lexical binding which is
caught in B, and then another, special binding. In terms of LET:

(let ((%a% 3))
(let ((b #'(lambda () %a%))
(%a% %a%))
(declare (special %a%))
b)))

So, really, if the special declaration was not in effect in subsequent
initforms, then not only can you tell that, but it must also be the
case that there is a lexical binding, because you can catch it: there
must actually be two bindings done.

I think it's clear that onmly one binding is done, and that the
declarations concerning it get `raised' to the point where the binding
is done:

(let* ((%a% 1)
(b (foo)))
(declare (special %a%))
b))

->

(let ((%a% 1))
(declare (special %a%))
(let ((b (foo)))
b)))

But I admit that I'd like something slightly more explicit than this
(and I expect someone who is better at reading the spec than me will
find something...)

--tim

Kaz Kylheku

unread,
Oct 10, 2002, 12:51:58 PM10/10/02
to
Edi Weitz <e...@agharta.de> wrote in message news:<87ofa2k...@bird.agharta.de>...
> behaved identically, i.e. as if in the LET* form *A* is already
> special when B is bound.
>
> However, rather than fiddling around with a finite number of
> implementations I'd prefer to find the place in the standard where
> this behaviour is explained. Unfortunately, I'm not sure where to look
> at. The closest thing I came across was the sentence
>
> "For LET*, a variable's scope also includes the remaining initial
> value forms for subsequent variable bindings."

Why do you want to write code that depends on someone's interpretation
of a gray area in the programming language? Regardless of what
assurances you receive in response to your question, you will still
have to fiddle with implementations.

Do you have something against writing

(defvar *a*)

at the top level?

Edi Weitz

unread,
Oct 10, 2002, 1:22:29 PM10/10/02
to
k...@ashi.footprints.net (Kaz Kylheku) writes:

The usual consensus in this newsgroup seems to be that it is better to
understand and follow the standard than to try and find out how your
"implementation du jour" copes with it (as opposed to languages
defined by their implementation like Perl). I think this is a good
thing and therefore I'm kind of surprised by your answer.

As a matter of fact I didn't consider this to be a gray area in the
language,[1] I just thought I had yet to find the place in the spec
where it is clearly explained. The fact that five different
implementations yield the same result seems to support the assumption
that what was unclear to me maybe wasn't unclear to those writing
these systems.

> Do you have something against writing
>
> (defvar *a*)
>
> at the top level?

I thought it was a good style convention to pinpoint the places where
special variables are used by explicit declarations in order not to
confuse someone (maybe yourself) who might want to read your code in a
couple of years. Yes, I know about the *...* convention, but still...

Cheers,
Edi.

[1] Indeed, I came across this issue by chance when I accidently
merged two LET forms into one LET* form and only later realized
that the code still worked although I didn't take care of the
(position of) the SPECIAL declaration.

Kalle Olavi Niemitalo

unread,
Oct 10, 2002, 2:00:25 PM10/10/02
to
Edi Weitz <e...@agharta.de> writes:

> I'd prefer to find the place in the standard where this
> behaviour is explained.

Did you look at 3.3.4 Declaration Scope?

Edi Weitz

unread,
Oct 10, 2002, 2:55:16 PM10/10/02
to

No, I didn't. Thanks, that was exactly what I was looking for!!

Edi.

Christopher Browne

unread,
Oct 10, 2002, 4:33:53 PM10/10/02
to
Oops! Edi Weitz <e...@agharta.de> was seen spray-painting on a wall:

> [1] Indeed, I came across this issue by chance when I accidently
> merged two LET forms into one LET* form and only later realized
> that the code still worked although I didn't take care of the
> (position of) the SPECIAL declaration.

What I find curious is that the LET* form doesn't expand into a nested
LET form.

I know that's how let* is defined in Scheme, and that view of it has
been useful to my understanding; I'm a bit surprised that I don't get
anything particularly interesting out of:

* (macroexpand '(let* ((a 1) (b (* a 3))) (+ (* a 7.2) (* b 42)))))
or
* (macroexpand-1 '(let* ((a 1) (b (* a 3))) (+ (* a 7.2) (* b 42)))))
--
(reverse (concatenate 'string "gro.mca@" "enworbbc"))
http://cbbrowne.com/info/spiritual.html
"Whip me. Beat me. Make me maintain AIX." -- Stephan Zielinski

Christophe Rhodes

unread,
Oct 10, 2002, 4:38:24 PM10/10/02
to
Christopher Browne <cbbr...@acm.org> writes:

> Oops! Edi Weitz <e...@agharta.de> was seen spray-painting on a wall:
> > [1] Indeed, I came across this issue by chance when I accidently
> > merged two LET forms into one LET* form and only later realized
> > that the code still worked although I didn't take care of the
> > (position of) the SPECIAL declaration.
>
> What I find curious is that the LET* form doesn't expand into a nested
> LET form.

But this is the point of this thread! They're not the same, because of
(let* ((!a! 'foo)
(b (lambda () !a!)))
(declare (special !a!))
... stuff possibly setting !a! and funcalling b)
and, OK, you _could_ parse the declarations, and expand that way, but
I'd expect it to be generally easier to implement inside the compiler.

Cheers,

Christophe
--
http://www-jcsu.jesus.cam.ac.uk/~csr21/ +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%") (pprint #36rJesusCollegeCambridge)

Barry Margolin

unread,
Oct 10, 2002, 4:40:08 PM10/10/02
to
In article <ao4o7h$jeb62$1...@ID-125932.news.dfncis.de>,

Christopher Browne <cbbr...@acm.org> wrote:
>Oops! Edi Weitz <e...@agharta.de> was seen spray-painting on a wall:
>> [1] Indeed, I came across this issue by chance when I accidently
>> merged two LET forms into one LET* form and only later realized
>> that the code still worked although I didn't take care of the
>> (position of) the SPECIAL declaration.
>
>What I find curious is that the LET* form doesn't expand into a nested
>LET form.

The declarations make this tricky, because they would have to be extracted
from the body and reinserted into the level corresponding to the variable
being bound. E.g. if you have:

(let* ((a ...)
(b ...))
(declare (special a b))
<body>)

it would have to expand into:

(let ((a ...))
(declare (special a))
(let ((b ...))
(declare (special b))
<body>))

--
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,
Oct 10, 2002, 5:39:06 PM10/10/02
to
* Edi Weitz

| From playing around with CMUCL, SBCL, LispWorks, AllegroCL, and CLISP it
| looks to me as if BAR-1 and BAR-2 in the following example
|
| (defun foo ()
| (declare (special *a*))
| *a*)
|
| (defun bar-1 ()
| (let ((*a* 37))
| (declare (special *a*))
| (let ((b (foo)))
| b)))
|
| (defun bar-2 ()
| (let* ((*a* 37)
| (b (foo)))
| (declare (special *a*))
| b))
|
| behaved identically, i.e. as if in the LET* form *A* is already
| special when B is bound.

It appears that you have received an answer to your question, but in case
there is still some confusion, do or did you think that

(let ((a 42))
(let ((b (foo)))
(declare (special a))
b))

should yield a different result? If yes, why?

--
Erik Naggum, Oslo, Norway

Act from reason, and failure makes you rethink and study harder.
Act from faith, and failure makes you blame someone and push harder.

Edi Weitz

unread,
Oct 10, 2002, 7:39:09 PM10/10/02
to
Erik Naggum <er...@naggum.no> writes:

Yes, I would have expected this to signal an error because *A* isn't
bound when FOO is called[1], i.e. *A* is only declared to be special
in the body of the second LET form but not in its init-form. A quick
test with CMUCL, AllegroCL, and CLISP shows that they seem to agree
with me.

What caused my initial confusion was that it wasn't clear to me that
(and why) the same thing doesn't happen in BAR-2 above. After having
been pointed to 3.3.4 in the CLHS and after reading about the scope of
the variables in a LET* form I _think_ that I now understand what's
going on. Or am I still wrong?

Maybe I should add that I loaded the three DEFUNs above into a fresh
image, i.e. there was no DEFPARAMETER or DEFVAR before which made *A*
globally special. Now that I think of it I'm not so sure anymore
whether the convention of enclosing variable names with asterisks
applies to all special variables or only to those which are _globally_
special. My apologies if I've caused any confusion by this.

Edi.

[1]: I take it that you meant *A* and not A and that the asterisks got
lost somehow, either in my Emacs or yours due to Gnus' somewhat
peculiar behaviour.

Erik Naggum

unread,
Oct 10, 2002, 8:00:34 PM10/10/02
to
* Edi Weitz <e...@agharta.de>

| Yes, I would have expected this to signal an error because *A* isn't
| bound when FOO is called[1], i.e. *A* is only declared to be special in
| the body of the second LET form but not in its init-form. A quick test
| with CMUCL, AllegroCL, and CLISP shows that they seem to agree with me.

Good answer.

| What caused my initial confusion was that it wasn't clear to me that (and
| why) the same thing doesn't happen in BAR-2 above. After having been
| pointed to 3.3.4 in the CLHS and after reading about the scope of the
| variables in a LET* form I _think_ that I now understand what's going on.
| Or am I still wrong?

I think the most important issue here is that there is only one `declare´
statement that might apply, and since bindings are made in sequence
rather than in parallel, that should apply to the declarations, too.

| Maybe I should add that I loaded the three DEFUNs above into a fresh
| image, i.e. there was no DEFPARAMETER or DEFVAR before which made *A*
| globally special. Now that I think of it I'm not so sure anymore whether
| the convention of enclosing variable names with asterisks applies to all
| special variables or only to those which are _globally_ special.

Well, I hunt for the `defvar´ or `defparameter´ when there are *'s around
a variable name and think they should not be used when the variables so
used are "internal".

| [1]: I take it that you meant *A* and not A and that the asterisks got
| lost somehow, either in my Emacs or yours due to Gnus' somewhat
| peculiar behaviour.

Amusingly, you are quite correct about the *boldface* thing in Gnus.
Maybe we should convince the world to use a rich language to communicate
font details and such, but these days that would be something ending in
ML and it is probably better to uninvent that whole thing and get rid of
the desire to use boldface. There is a reason why I use /italics/.

Tim Bradshaw

unread,
Oct 11, 2002, 4:19:57 AM10/11/02
to
k...@ashi.footprints.net (Kaz Kylheku) wrote in message news:<cf333042.02101...@posting.google.com>...

>
> Do you have something against writing
>
> (defvar *a*)
>
> at the top level?

Not all special variables are (or should be) globally special.

--tim

Barry Margolin

unread,
Oct 11, 2002, 10:04:05 AM10/11/02
to
In article <87fzvd2...@bird.agharta.de>, Edi Weitz <e...@agharta.de> wrote:
>Maybe I should add that I loaded the three DEFUNs above into a fresh
>image, i.e. there was no DEFPARAMETER or DEFVAR before which made *A*
>globally special. Now that I think of it I'm not so sure anymore
>whether the convention of enclosing variable names with asterisks
>applies to all special variables or only to those which are _globally_
>special. My apologies if I've caused any confusion by this.

The asterisks are a hint that the variable was proclaimed special. If
there's a special declaration within a few lines of all the uses of the
variable, this hint isn't as necessary. I don't think local special
declarations are too common, so there isn't much widespread convention for
how to name them.

0 new messages