Subject: Re: `let*' and functional design
From: rpw3@rpw3.org (Rob Warnock)
Date: Tue, 12 Jul 2005 01:15:22 -0500
Newsgroups: comp.lang.lisp
Message-ID: <RqWdnSiqNvpnwk7fRVn-gA@speakeasy.net>
Kalle Olavi Niemitalo  <kon@iki.fi> wrote:
+---------------
| rpw3@rpw3.org (Rob Warnock) writes:
| > That is, when used badly you end up with this:
| >     (let* ((tmp (create-first-value))
| >            (tmp (some-function tmp))
| >            (tmp (some-other-function tmp))
| >            (tmp (some-last-function tmp)))
| >       ...use TMP ...)
| 
| Does the standard actually allow this; and if so, does it specify
| which of the bindings is used for declarations and other
| references to TMP in the body?  Section 3.1.5 "Shadowing" does
| not apply here, because all the bindings are established by the
| same form.
+---------------

Interesting question!! The CLHS certainly doesn't *disallow* it.

Hmmm... I would argue that it does implicitly allow it because of the
phrase in "Special Operator LET, LET*" that says:

    LET performs the bindings in parallel and LET* does them sequentially.
							     ************

Now if you look at what a "binding" is, especially a lexical binding,
I don't see how you could come to any conclusion other than that in a
LET* a subsequent binding of the same name is a *different* binding,
because it is sequentially later and therefore is a *new* binding [which
therefore shadows any existing lexical bindings of the same name]. That
is, I claim that the above CLHS phrase is all you need to equate this:

    (let* ((x val1)
	   (x val2))
      ...body...)

with this:

    (let ((x val1))
      (let ((x val2))
        ...body...)

I would also claim that CLHS "3.1.5 Shadowing" *does* apply, though
perhaps not as clearly, because the bindings of a LET* are "sequential",
which is equivalent to 3.1.5's "nested".

+---------------
| ... and if so, does it specify which of the bindings is used
| for declarations and other references to TMP in the body?
+---------------

If I'm correct, I'd have to conclude that only the textually *last*
binding of a given variable name would be used by references in the body,
and therefore only the last binding should logically be affected by
declarations referencing that name. If you wanted intermediate TMPs to
be declared [at all, not just differently from the last!], you'd have to
break the LET* into two or more LET*'s and intersperse the declarations.
[Or do some manual renaming, which is probably simpler and more readable.]


-Rob

p.s. Every Lisp or Scheme I have ever tried allows this:

    > (let* ((x 10)		; 10
	     (x (/ x 2))	;  5
	     (x (1+ x))		;  6
	     (x (* x 3))	; 18
	     (x (- x 2)))	; 16
	(+ x 4))
    20
    >

though that's only a bunch of implementations, not a standard.  ;-}

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607