From: N. Raghavendra
Subject: `let*' and functional design
Date: 
Message-ID: <863bqpjwkk.fsf@riemann.mri.ernet.in>
I was reading Paul Graham's book `On Lisp', and found the following
passage in Chapter 3, `Functional Programming':

  "Having functional programming as an ideal doesn't imply that
   programs should never have side-effects. It just means that they
   should have no more than necessary.

   "It may take time to develop this habit.  One way to start is to
   treat the following operators as if there were a tax on their use:
   set setq setf psetf psetq incf decf push pop pushnew rplaca rplacd
   rotatef shiftf remf remprop remhash and also let*, in which
   imperative programs often lie concealed. Treating these operators
   as taxable is only proposed as a help toward, not a criterion for,
   good Lisp style.  However, this alone can get you surprisingly
   far."

This seems to indicate that the use of `let*' is deprecated as far as
functional programming is concerned.  Why is this so?  Is it common
practice not to use `let*'?

Raghavendra.

-- 
N. Raghavendra <·····@mri.ernet.in> | See message headers for contact
Harish-Chandra Research Institute   | and OpenPGP details.

From: Christopher Koppler
Subject: Re: `let*' and functional design
Date: 
Message-ID: <1120829606.331923.299370@g47g2000cwa.googlegroups.com>
N. Raghavendra schrieb:
> I was reading Paul Graham's book `On Lisp', and found the following
> passage in Chapter 3, `Functional Programming':
>
>   "Having functional programming as an ideal doesn't imply that
>    programs should never have side-effects. It just means that they
>    should have no more than necessary.
>
>    "It may take time to develop this habit.  One way to start is to
>    treat the following operators as if there were a tax on their use:
>    set setq setf psetf psetq incf decf push pop pushnew rplaca rplacd
>    rotatef shiftf remf remprop remhash and also let*, in which
>    imperative programs often lie concealed. Treating these operators
>    as taxable is only proposed as a help toward, not a criterion for,
>    good Lisp style.  However, this alone can get you surprisingly
>    far."
>
> This seems to indicate that the use of `let*' is deprecated as far as
> functional programming is concerned.  Why is this so?  Is it common
> practice not to use `let*'?

If CL would be a more purely functional language, very probably so.
Then, however, let* would maybe not be in the language. Since CL is a
multi-paradigm language, programmers can use the paradigm the suits the
task at hand; if a problem cries for a functional solution, use of let*
is definitely suspicious IMO.

-- 
Christopher
From: Mike
Subject: Re: `let*' and functional design
Date: 
Message-ID: <o7xze.31988$B_3.16323@fe05.lga>
On 2005-07-08, Christopher Koppler <········@chello.at> wrote:
>
> N. Raghavendra schrieb:
>> I was reading Paul Graham's book `On Lisp', and found the following
>> passage in Chapter 3, `Functional Programming':
>>
>>   "Having functional programming as an ideal doesn't imply that
>>    programs should never have side-effects. It just means that they
>>    should have no more than necessary.
>>
>>    "It may take time to develop this habit.  One way to start is to
>>    treat the following operators as if there were a tax on their use:
>>    set setq setf psetf psetq incf decf push pop pushnew rplaca rplacd
>>    rotatef shiftf remf remprop remhash and also let*, in which
>>    imperative programs often lie concealed. Treating these operators
>>    as taxable is only proposed as a help toward, not a criterion for,
>>    good Lisp style.  However, this alone can get you surprisingly
>>    far."
>>
>> This seems to indicate that the use of `let*' is deprecated as far as
>> functional programming is concerned.  Why is this so?  Is it common
>> practice not to use `let*'?
>
> If CL would be a more purely functional language, very probably so.
> Then, however, let* would maybe not be in the language. Since CL is a
> multi-paradigm language, programmers can use the paradigm the suits the
> task at hand; if a problem cries for a functional solution, use of let*
> is definitely suspicious IMO.
>

What problems are suited for a purely functional soltion? I don't really
understand the distinctions between what is and is not functional. The
way I code I try to get things done quickly, without problem, that can
be extended if needed. If possible the solution also needs to be elegant.

Mike
From: William Bland
Subject: Re: `let*' and functional design
Date: 
Message-ID: <pan.2005.07.08.15.50.26.52319@abstractnonsense.com>
On Fri, 08 Jul 2005 15:40:04 +0000, Mike wrote:
> 
> What problems are suited for a purely functional soltion? I don't really
> understand the distinctions between what is and is not functional. The
> way I code I try to get things done quickly, without problem, that can
> be extended if needed. If possible the solution also needs to be elegant.

I tend to write things as functionally as I can while keeping code clear
for one reason:  Functional code is easier to test.

One reason I still tend to write iteration in a slightly schemey way, and
have been a little reluctant to pick up LOOP is that the scheme way is
more testable:  you can write a test case that effectively jumps into the
middle of a loop and tests just a single iteration.  On the other hand,
it's non-idiomatic CL, so I'm gradually using LOOP more and more so that
other people can read my code.

Best wishes,
		Bill.
From: Joe Marshall
Subject: Re: `let*' and functional design
Date: 
Message-ID: <zmsxhxgv.fsf@comcast.net>
Mike <·····@mikee.ath.cx> writes:

> What problems are suited for a purely functional soltion? 

Problems that do not have an implicit dependency upon time.

> I don't really understand the distinctions between what is and is
> not functional.

Mathematically, the result of a `function' depends *only* on the
arguments.  For instance, this is a function:

   (defun foo (x y)
     (+ (sin x) (sqrt y)))

If I call foo on 2 and 3, I get 2.641348234394559 every time.

But suppose I am modeling a bank account (the classic example).  I
have a procedure (a computer `function' that is not a mathematical
function) that tells me my balance and one that I use to deposit
money.

(get-balance 'jrm) => 0.00

(deposit 5.21 'jrm)

(get-balance 'jrm) => 5.21

(deposit 3.98 'jrm)

(get-balance 'jrm) => 9.19

GET-BALANCE is *not* a mathematical function because the result is
different depending on how many times I called deposit.

> The way I code I try to get things done quickly, without problem,
> that can be extended if needed.  If possible the solution also needs
> to be elegant.

Mathematical functions have good properties.  Since they always
compute the same thing when you give them the same input, you
completely avoid a whole set of really nasty time-dependent bugs.
Mathematical functions compose:  a function composed with a function is
also a function.  This makes it easier to create large functional
programs by simply composing smaller ones.  Functional programs extend
easily:  you cannot `break' an existing functional program by adding
more functions.

The downside is this:  functional programs take a bit of practice,
especially if you are used to imperative style.  Functional programs
are generally more memory intensive than imperative ones (this is
*much* less of a problem than it was 20 years ago).  If you don't pay
attention, functional programs can be horrendously slow.  It is easy
to write functional programs that perform *far* too much work.


-- 
~jrm
From: Matthias
Subject: Re: `let*' and functional design
Date: 
Message-ID: <36wfyupthrp.fsf@hundertwasser.ti.uni-mannheim.de>
Mike <·····@mikee.ath.cx> writes:

> What problems are suited for a purely functional soltion? I don't really
> understand the distinctions between what is and is not functional. 

Well, at some point programs have to read input and generate output.
But apart from that they can always be formulated "purely functional"
(i.e., so that they do not work by changing the state of variables).

With me, it depends much more on the mindset/language I'm in than on
the specific problem at hand if I choose a functional or a procedural
solution.

A nice purely functional solution to a toy problem is described by
Hudak and Jones in "Haskell vs. Ada vs. C++ vs. Awk vs. ... An
Experiment in Software Prototyping Productivity", available online
http://haskell.org/papers/NSWC/jfp.ps 

Matthias
From: ··········@gmail.com
Subject: Re: `let*' and functional design
Date: 
Message-ID: <1121008690.655497.238420@g47g2000cwa.googlegroups.com>
That's a funny paper.  Read the explanation for why Relational Lisp
beat Haskell (it took 3 hours to code a solution in Relational Lisp, 8
hours in Haskell), and imagine what it would have said if things had
turned out the other way around.
From: Matthias Buelow
Subject: Re: `let*' and functional design
Date: 
Message-ID: <86eka9h0sj.fsf@drjekyll.mkbuelow.net>
"Christopher Koppler" <········@chello.at> writes:

>If CL would be a more purely functional language, very probably so.
>Then, however, let* would maybe not be in the language. Since CL is a
>multi-paradigm language, programmers can use the paradigm the suits the
>task at hand; if a problem cries for a functional solution, use of let*
>is definitely suspicious IMO.

Why? I can't see any connection between let* and imperative
programming style. In fact, I'd think that functional programs would
make more use of let*, since they would not update bindings
established by let, and hence have more need of nested bindings.

mkb.
From: Pascal Costanza
Subject: Re: `let*' and functional design
Date: 
Message-ID: <3j7nq8Foqvf2U1@individual.net>
Matthias Buelow wrote:

> "Christopher Koppler" <········@chello.at> writes:
> 
>>If CL would be a more purely functional language, very probably so.
>>Then, however, let* would maybe not be in the language. Since CL is a
>>multi-paradigm language, programmers can use the paradigm the suits the
>>task at hand; if a problem cries for a functional solution, use of let*
>>is definitely suspicious IMO.
> 
> Why? I can't see any connection between let* and imperative
> programming style. In fact, I'd think that functional programs would
> make more use of let*, since they would not update bindings
> established by let, and hence have more need of nested bindings.

Cf. discussions about order of evaluation in Scheme. The "danger" 
(sorry, no pun intended) of let* is that you start to consciously or 
unconsciously rely on the order of evalution of subexpressions. In a 
strict and pure understanding, this is not functional.


Pascal

-- 
2nd European Lisp and Scheme Workshop
July 26 - Glasgow, Scotland - co-located with ECOOP 2005
http://lisp-ecoop05.bknr.net/
From: Matthias Buelow
Subject: Re: `let*' and functional design
Date: 
Message-ID: <86ackxp87t.fsf@drjekyll.mkbuelow.net>
Pascal Costanza <··@p-cos.net> writes:

>Cf. discussions about order of evaluation in Scheme. The "danger"
>(sorry, no pun intended) of let* is that you start to consciously or
>unconsciously rely on the order of evalution of subexpressions. In a
>strict and pure understanding, this is not functional.

This is hilarious; function composition implies an ordering, that is,
generally f o g != g o f, unless f=g, and let* is little more than
syntactic sugar for (lambda (...) (lambda (...) (...))) so I really
can't see a point here.

mkb.
From: Pascal Costanza
Subject: Re: `let*' and functional design
Date: 
Message-ID: <3j7ov8Foq19oU1@individual.net>
Matthias Buelow wrote:
> Pascal Costanza <··@p-cos.net> writes:
> 
>>Cf. discussions about order of evaluation in Scheme. The "danger"
>>(sorry, no pun intended) of let* is that you start to consciously or
>>unconsciously rely on the order of evalution of subexpressions. In a
>>strict and pure understanding, this is not functional.
> 
> This is hilarious; function composition implies an ordering, that is,
> generally f o g != g o f, unless f=g, and let* is little more than
> syntactic sugar for (lambda (...) (lambda (...) (...))) so I really
> can't see a point here.

Maybe. I don't really care either.


Pascal

-- 
2nd European Lisp and Scheme Workshop
July 26 - Glasgow, Scotland - co-located with ECOOP 2005
http://lisp-ecoop05.bknr.net/
From: Karl A. Krueger
Subject: Re: `let*' and functional design
Date: 
Message-ID: <danhu5$p78$1@baldur.whoi.edu>
Matthias Buelow <···@incubus.de> wrote:
> Pascal Costanza <··@p-cos.net> writes:
>>Cf. discussions about order of evaluation in Scheme. The "danger"
>>(sorry, no pun intended) of let* is that you start to consciously or
>>unconsciously rely on the order of evalution of subexpressions. In a
>>strict and pure understanding, this is not functional.
> 
> This is hilarious; function composition implies an ordering, that is,
> generally f o g != g o f, unless f=g, and let* is little more than
> syntactic sugar for (lambda (...) (lambda (...) (...))) so I really
> can't see a point here.

These seem to be unrelated points.  Mathematical function composition is
generally taken to be an operation on functions of one argument.  Order
of evaluation shows up as an ambiguity with functions of multiple
arguments -- or expressions such as LET which are equivalent to the
evaluation of a function with multiple arguments.

LET* is a macro which conceals arbitrarily deep *nesting* of evaluation.
A LET* form with three bindings is equivalent to three nested LAMBDAs,
rather than the single LAMBDA of three arguments which a LET would be
equivalent to.  The whole point of LET* is to convert a piece of list
structure that looks parallel (a set of bindings) into a nested one.

-- 
Karl A. Krueger <········@example.edu> { s/example/whoi/ }
From: Matthias Buelow
Subject: Re: `let*' and functional design
Date: 
Message-ID: <8664vifiky.fsf@drjekyll.mkbuelow.net>
"Karl A. Krueger" <········@example.edu> writes:

>LET* is a macro which conceals arbitrarily deep *nesting* of evaluation.
>A LET* form with three bindings is equivalent to three nested LAMBDAs,
>rather than the single LAMBDA of three arguments which a LET would be
>equivalent to.  The whole point of LET* is to convert a piece of list
>structure that looks parallel (a set of bindings) into a nested one.

Errm yes? That's what I was trying to say.
And why is that contradicting functional programming?

mkb.
From: Hannah Schroeter
Subject: Re: `let*' and functional design
Date: 
Message-ID: <dau45k$fn$2@c3po.use.schlund.de>
Hello!

Karl A. Krueger <········@example.edu> wrote:
>[...]

>LET* is a macro which conceals arbitrarily deep *nesting* of evaluation.
>A LET* form with three bindings is equivalent to three nested LAMBDAs,
>rather than the single LAMBDA of three arguments which a LET would be
>equivalent to.  The whole point of LET* is to convert a piece of list
>structure that looks parallel (a set of bindings) into a nested one.

In the end, it conceals value dependencies. Same as Haskell's way
of let/where (where dependencies, even mutual dependencies of the
bindings, are possible). And what language is nearer to being
purely functional than Haskell?

Kind regards,

Hannah.
From: Hannah Schroeter
Subject: Re: `let*' and functional design
Date: 
Message-ID: <dau42j$fn$1@c3po.use.schlund.de>
Hello!

Pascal Costanza  <··@p-cos.net> wrote:
>Matthias Buelow wrote:

>> "Christopher Koppler" <········@chello.at> writes:

>>>If CL would be a more purely functional language, very probably so.
>>>Then, however, let* would maybe not be in the language. Since CL is a
>>>multi-paradigm language, programmers can use the paradigm the suits the
>>>task at hand; if a problem cries for a functional solution, use of let*
>>>is definitely suspicious IMO.

>> Why? I can't see any connection between let* and imperative
>> programming style. In fact, I'd think that functional programs would
>> make more use of let*, since they would not update bindings
>> established by let, and hence have more need of nested bindings.

>Cf. discussions about order of evaluation in Scheme. The "danger" 
>(sorry, no pun intended) of let* is that you start to consciously or 
>unconsciously rely on the order of evalution of subexpressions. In a 
>strict and pure understanding, this is not functional.

Then, you could use this argument against CL's let, too, cause that
also guarantees the order of evaluation.

Sorry, I don't agree to deeming let* "more un-functional" than let.

I prefer

(let* ((a some-expression)
       (b something-using-a)
       (c something-else-using-a))
  ...)

over

(let ((a some-expression))
  (let ((b something-using-a)
        (c something-else-using-a))
    ...))

>Pascal

Kind regards,

Hannah.
From: ··············@hotmail.com
Subject: Re: `let*' and functional design
Date: 
Message-ID: <1121135926.185775.81630@z14g2000cwz.googlegroups.com>
Hannah Schroeter wrote:
> Sorry, I don't agree to deeming let* "more un-functional" than let.

The original statement by Graham (_On Lisp_, p. 32) is not simply that
let* is "less functional", rather, that it can be used to clothe a
procedural algorithm in functional clothing. I.e. many functions in
procedural languages work by sequential assignments, which get dressed
up as let* sequential bindings.

Pedagogically, Graham is trying to avoid students writing
thinly-disguised Fortran or Pascal programs in Lisp, to break their
habit of thinking sequentially in small steps, and to guide them toward
*thinking* functionally, and hence, to write more elegant Lisp code.

The idea is that by the time the student arrives at the need to write a
function with deep sequential dependencies, he will use let* because it
is the most Lispy solution; not because it is the way to write
Fortran-in-Lisp.
From: Kaz Kylheku
Subject: Re: `let*' and functional design
Date: 
Message-ID: <1120840967.727769.139910@z14g2000cwz.googlegroups.com>
N. Raghavendra wrote:
> I was reading Paul Graham's book `On Lisp', and found the following
> passage in Chapter 3, `Functional Programming':
>
>   "Having functional programming as an ideal doesn't imply that
>    programs should never have side-effects. It just means that they
>    should have no more than necessary.
>
>    "It may take time to develop this habit.  One way to start is to
>    treat the following operators as if there were a tax on their use:
>    set setq setf psetf psetq incf decf push pop pushnew rplaca rplacd
>    rotatef shiftf remf remprop remhash and also let*, in which
>    imperative programs often lie concealed. Treating these operators
>    as taxable is only proposed as a help toward, not a criterion for,
>    good Lisp style.  However, this alone can get you surprisingly
>    far."
>
> This seems to indicate that the use of `let*' is deprecated as far as
> functional programming is concerned.  Why is this so?  Is it common
> practice not to use `let*'?

LET* is not an operator for imperative programming. It is perfectly
compatible with functional programming. The only difference is scoping;
that the variable clauses can refer to bindings set up by lexically
prior ones.

The reason imperative programs can hide inside LET* is that the
expressions that establish the variable values are evaluated strictly
left to right. But this is true of LET also; imperative logic can be
hidden in LET. The only difference is the scoping of the binding.

LET* can also clarify functional code by unravelling too many levels of
nesting, and giving meaningful names to the intermediate values.

It can reduce code. For example, suppose you have a bunch of
computations with a large common term. Use LET* to compute the common
term, and then refer to that term.

  (let* ((big-divisor (big-formula ...))
         (x (/ (formula-1) big-divisor))
         (y (/ (formula-2) big-divisor))
         (z (/ (formula-3) big-divisor)))
    ...)

Where is the hidden imperative program? All is see is run-of-the mill
chalkboard mathematics expressed in Lisp.

Note that some functional languages allow for even more intricate
lexical backreferences than LET*, to support the one-step construction
of objects that contain references to themselves! Without the
backreferences, the object would have to be constructed without the
backreferences in one step, and then destructively patched with the
backreferences in a second step.

Why should I imagine that there is a tax on the use of LET* which
actually reduces how much I have to type, and how deeply I have to
nest? Let's measure real costs and consider those to be a tax, rather
than imagining taxes that aren't there.

I don't see how you can lump LET* in the same category as RPLACA.
That's just weird.
From: jayessay
Subject: Re: `let*' and functional design
Date: 
Message-ID: <m3hdf5uq5g.fsf@rigel.goldenthreadtech.com>
"Kaz Kylheku" <········@gmail.com> writes:

<more good stuff snipped...>

> Why should I imagine that there is a tax on the use of LET* which
> actually reduces how much I have to type, and how deeply I have to
> nest? Let's measure real costs and consider those to be a tax, rather
> than imagining taxes that aren't there.
> 
> I don't see how you can lump LET* in the same category as RPLACA.
> That's just weird.

I absolutely agree.  It's hard to imagine PG's motivation for doing
so; I mean he can't possibly be that clueless.

Frankly, I think the problem with LET* is the *.  It would have been
better if LET* had been LET and LET had been LET*, but whatever.


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: Rob Warnock
Subject: Re: `let*' and functional design
Date: 
Message-ID: <jvOdnUMvIYzEY0zfRVn-tQ@speakeasy.net>
jayessay  <······@foo.com> wrote:
+---------------
| "Kaz Kylheku" <········@gmail.com> writes:
| > I don't see how you can lump LET* in the same category as RPLACA.
| > That's just weird.
| 
| I absolutely agree.  It's hard to imagine PG's motivation for doing
| so; I mean he can't possibly be that clueless.
| 
| Frankly, I think the problem with LET* is the *.  It would have been
| better if LET* had been LET and LET had been LET*, but whatever.
+---------------

Or maybe LET & PLET, by analogy to SETQ & PSETQ or SETF & PSETF.

By the way, LET* can encourage a sort of SSA ["Static Single Assignment",
see <http://en.wikipedia.org/wiki/Static_single_assignment_form>] style
of programming that gets rid of a bunch of unnecessary mutations (SETFs)
by giving each new result either a new name or a new scope for the old
name. So if you're doing some kind of "step by step" algorithm, at least
you can use rebinding/renaming instead of mutation [which has potential
performance advantages for generational collectiors, by the way]. Though
as others have noted it can also create readability issues due to the
renaming. 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 ...)

or worse, some mix of temp names, with the data flow all mixed up:

    (let* ((tmp0 (create-first-value))
	   (tmp1 (another-first-value))
	   (tmp0 (some-function tmp0))
	   (tmp0 (some-other-function tmp0))
	   (tmp0 (some-function tmp0 tmp1))
	   (tmp1 (some-third-function tmp0))
	   (tmp (some-last-function tmp0 tmp1)))
      ...use TMP ...)

On the other hand, deep-but-readable LET* style is quite commonly
seen in XLIB-based windowing code:

   (let* ((display (server-display server))
	  (screen (server-screen server))
	  (fg-color (server-black server))
	  (bg-color (server-white server))
	  (font (open-font display font))
	  (text-height (+ (max-char-ascent font) (max-char-descent font)))
	  (text-width (text-width font "Thu Jan  1 00:00:00 1970"))
	  (height (* 2 (+ text-height text-margin)))
	  (width (+ text-width (* 2 text-margin)))
	  (window (create-window :parent (screen-root screen)
				 :x x :y y :width width :height height
				 :background bg-color))
	  (gc (create-gcontext :drawable window
			       :background bg-color
			       :foreground fg-color
			       :font font)))
     (set-wm-properties window :name title
			       :icon-name "simple clock"
			       :user-specified-position-p t)
     (map-window window)
     (make-clock :display display :screen screen
                 :window window :gc gc
                 :x x :y y :height height :width width
                 :title title :font font
                 :bg-color bg-color :fg-color fg-color
                 :text-x text-margin
                 :text-y (+ (max-char-ascent font) text-margin)))


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Kalle Olavi Niemitalo
Subject: Re: `let*' and functional design
Date: 
Message-ID: <87oe991mxf.fsf@Astalo.kon.iki.fi>
····@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.
From: Rob Warnock
Subject: Re: `let*' and functional design
Date: 
Message-ID: <RqWdnSiqNvpnwk7fRVn-gA@speakeasy.net>
Kalle Olavi Niemitalo  <···@iki.fi> wrote:
+---------------
| ····@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.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Pascal Bourguignon
Subject: Re: `let*' and functional design
Date: 
Message-ID: <87eka9nmlv.fsf@thalassa.informatimago.com>
"Kaz Kylheku" <········@gmail.com> writes:
> Why should I imagine that there is a tax on the use of LET* which
> actually reduces how much I have to type, and how deeply I have to
> nest? Let's measure real costs and consider those to be a tax, rather
> than imagining taxes that aren't there.

Indeed.


> I don't see how you can lump LET* in the same category as RPLACA.
> That's just weird.

Because they are both as functional as the rest:

[172]>  (functional (let ((x (cons 1 2))) 
                      (print (rplaca x (+ (car x) 1)))
                      (print (rplaca x (+ (car x) 1)))
                      (print (rplaca x (+ (car x) 1)))))
1. Trace: (CONS* 'NIL '1 '2)
1. Trace: CONS* ==> (((1 . 2) 1 . 2)), (1 . 2)
1. Trace: (CAR* '(((1 . 2) 1 . 2)) '(1 . 2))
1. Trace: CAR* ==> (((1 . 2) 1 . 2)), 1
(2 . 2) 
1. Trace: (CAR* '(((1 . 2) 2 . 2) ((1 . 2) 1 . 2)) '(1 . 2))
1. Trace: CAR* ==> (((1 . 2) 2 . 2) ((1 . 2) 1 . 2)), 2
(3 . 2) 
1. Trace: (CAR* '(((1 . 2) 3 . 2) ((1 . 2) 2 . 2) ((1 . 2) 1 . 2)) '(1 . 2))
1. Trace: CAR* ==> (((1 . 2) 3 . 2) ((1 . 2) 2 . 2) ((1 . 2) 1 . 2)), 3
(4 . 2) 
(((1 . 2) 4 . 2) ((1 . 2) 3 . 2) ((1 . 2) 2 . 2) ((1 . 2) 1 . 2)) ;
(4 . 2)


[173]>  (macroexpand '(functional (let ((x (cons 1 2))) 
                      (print (rplaca x (+ (car x) 1)))
                      (print (rplaca x (+ (car x) 1)))
                      (print (rplaca x (+ (car x) 1))))))

;; If this is not functional, then I don't know what is...

(APPLY
 #'(LAMBDA (#:VENV16542 X)
    (LET
     ((#:VENVALS16559
       (MULTIPLE-VALUE-LIST
        (APPLY #'RPLACA*
         (MULTIPLE-VALUE-BIND (#:VENV16560 #:VVAL16561)
          (VALUES
           (LET
            ((#:VENVALS16551
              (MULTIPLE-VALUE-LIST
               (APPLY #'RPLACA*
                (MULTIPLE-VALUE-BIND (#:VENV16552 #:VVAL16553)
                 (VALUES
                  (LET
                   ((#:VENVALS16543
                     (MULTIPLE-VALUE-LIST
                      (APPLY #'RPLACA*
                       (MULTIPLE-VALUE-BIND (#:VENV16544 #:VVAL16545)
                        (VALUES #:VENV16542 X)
                        (LET
                         ((#:VENVALS16546
                           (MULTIPLE-VALUE-LIST
                            (LET
                             ((#:VENVALS16547
                               (MULTIPLE-VALUE-BIND (#:VENV16548 #:VVAL16549)
                                (APPLY #'CAR*
                                 (MULTIPLE-VALUE-LIST (VALUES #:VENV16544 X)))
                                (LET
                                 ((#:VENVALS16550
                                   (MULTIPLE-VALUE-LIST
                                    (VALUES #:VENV16548 1))))
                                 (CONS (CAR #:VENVALS16550)
                                  (CONS #:VVAL16549 (CDR #:VENVALS16550)))))))
                             (VALUES (CAR #:VENVALS16547)
                              (APPLY #'+ (CDR #:VENVALS16547)))))))
                         (CONS (CAR #:VENVALS16546)
                          (CONS #:VVAL16545 (CDR #:VENVALS16546)))))))))
                   (VALUES (CAR #:VENVALS16543)
                    (APPLY #'PRINT (CDR #:VENVALS16543))))
                  X)
                 (LET
                  ((#:VENVALS16554
                    (MULTIPLE-VALUE-LIST
                     (LET
                      ((#:VENVALS16555
                        (MULTIPLE-VALUE-BIND (#:VENV16556 #:VVAL16557)
                         (APPLY #'CAR*
                          (MULTIPLE-VALUE-LIST (VALUES #:VENV16552 X)))
                         (LET
                          ((#:VENVALS16558
                            (MULTIPLE-VALUE-LIST (VALUES #:VENV16556 1))))
                          (CONS (CAR #:VENVALS16558)
                           (CONS #:VVAL16557 (CDR #:VENVALS16558)))))))
                      (VALUES (CAR #:VENVALS16555)
                       (APPLY #'+ (CDR #:VENVALS16555)))))))
                  (CONS (CAR #:VENVALS16554)
                   (CONS #:VVAL16553 (CDR #:VENVALS16554)))))))))
            (VALUES (CAR #:VENVALS16551) (APPLY #'PRINT (CDR #:VENVALS16551))))
           X)
          (LET
           ((#:VENVALS16562
             (MULTIPLE-VALUE-LIST
              (LET
               ((#:VENVALS16563
                 (MULTIPLE-VALUE-BIND (#:VENV16564 #:VVAL16565)
                  (APPLY #'CAR* (MULTIPLE-VALUE-LIST (VALUES #:VENV16560 X)))
                  (LET
                   ((#:VENVALS16566
                     (MULTIPLE-VALUE-LIST (VALUES #:VENV16564 1))))
                   (CONS (CAR #:VENVALS16566)
                    (CONS #:VVAL16565 (CDR #:VENVALS16566)))))))
               (VALUES (CAR #:VENVALS16563)
                (APPLY #'+ (CDR #:VENVALS16563)))))))
           (CONS (CAR #:VENVALS16562)
            (CONS #:VVAL16561 (CDR #:VENVALS16562)))))))))
     (VALUES (CAR #:VENVALS16559) (APPLY #'PRINT (CDR #:VENVALS16559)))))
 (MULTIPLE-VALUE-LIST
  (APPLY #'CONS*
   (MULTIPLE-VALUE-BIND (#:VENV16567 #:VVAL16568) (VALUES NIL 1)
    (LET ((#:VENVALS16569 (MULTIPLE-VALUE-LIST (VALUES #:VENV16567 2))))
     (CONS (CAR #:VENVALS16569) (CONS #:VVAL16568 (CDR #:VENVALS16569)))))))) ;
T
[174]> 


So, one could say that functional code is low level, and setq and
rplaca are high level functional abstractions, if one can judge by the LOC...


;;----------------------------------------------------------------------

(shadow '(let let*))

(defmacro let (vs &body forms)
  `((lambda ,(mapcar (function car) vs) ,@forms) ,@(mapcar (function cadr) vs)))

(defmacro let* (vs &body forms)
  (if vs `(let (,(car vs)) (let* ,(cdr vs) ,@forms))
    `(let () ,@forms)))


(defun cons* (env car cdr)
  (let ((cons (cons car cdr)))
    (values (cons (cons cons cons) env) cons)))

(defun car* (env cons)
  (if (null cons)
    (values env nil)
    (let ((assoc (assoc cons env)))
      (if assoc
        (values env (car (cdr assoc)))
        (error "applying car* to not a cons*")))))

(defun cdr* (env cons)
  (if (null cons)
    (values env nil)
    (let ((assoc (assoc cons env)))
      (if assoc
        (values env (cdr (cdr assoc)))
        (error "applying car* to not a cons*")))))

(defun rplaca* (env cons car)
  (let ((assoc (assoc cons env)))
    (if assoc
      (let* ((cons (car assoc))
            (cdr  (cdr (cdr assoc)))
            (ncons (cons car cdr)))
        (values (cons (cons cons ncons) env) ncons))
      (error "applying replaca* to not a cons*"))))

(defun rplacd* (env cons cdr)
  (let ((assoc (assoc cons env)))
    (if assoc
      (let ((cons (car assoc))
            (car  (car (cdr assoc)))
            (ncons (cons car cdr)))
        (values (cons (cons cons ncons) env) ncons))
      (error "applying replacd* to not a cons*"))))

(defparameter *map* `((cons   . cons*)
                      (car    . car*)
                      (cdr    . cdr*)
                      (rplaca . rplaca*)
                      (rplacd . rplacd*)))


(DEFMACRO WITH-GENSYMS (SYMS &BODY BODY)
  "
DO:      Replaces given symbols with gensyms. Useful for creating macros.
NOTE:    This version by Paul Graham in On Lisp."
  `(LET ,(MAPCAR (LAMBDA (S) `(,S (GENSYM ,(string s)))) SYMS) ,@BODY))


(defun make-functional-progn (env body)
  (if (null (cdr body))
    (make-functional-eval env (first body))
    (make-functional-progn (make-functional-eval env (first body)) (rest body))))


(defun make-functional-arguments (env arguments)
  ;; env (f a) (g b) (h c)
  ;;
  ;; (e1 fa) <- (f env a)
  ;; (e2 gb) <- (g e1  b)
  ;; (e3 hc) <- (h e2  c)
  ;; (e3 fa gb hc)
  (if (null (cdr arguments))
    `(multiple-value-list ,(make-functional-eval env (first arguments)))
    (with-gensyms (venv vval venvals)
      `(multiple-value-bind  (,venv ,vval)
           ,(make-functional-eval env (first arguments))
         (let ((,venvals ,(make-functional-arguments venv (rest arguments))))
           (cons (car ,venvals) (cons ,vval (cdr ,venvals))))))))


(defun make-functional-apply (env fun args)
  `(apply (function ,fun) ,(make-functional-arguments env args)))

(defun make-apply (env fun args)
  (with-gensyms (venvals)
    `(let ((,venvals ,(make-functional-arguments env args)))
       (values (car ,venvals) (apply (function ,fun) (cdr ,venvals))))))


(defun make-functional-eval (env expr)
  (cond
   ((atom expr) `(values ,env ,expr))
   ((eq (first expr) 'progn)
    (make-functional-progn env (rest expr)))
   ((eq (first expr) 'if)
    (with-gensyms (venv vcond)
      `(multiple-value-bind (,venv ,vcond)
           ,(make-functional-eval env (second expr))
         (if ,vcond
           ,(make-functional-eval venv (third sexpr))
           ,(make-functional-eval venv (fourth sexpr))))))
   ((and (consp (first expr)) (eq (first (first expr)) 'lambda))
    (let ((fun (first expr))
          (args (rest expr))
          (venv (gensym "VENV")))
      (make-functional-apply
       env
       `(lambda ,(cons venv (second fun))
          ,(make-functional-progn venv (rest (rest fun))))
       args)))
   ((macro-function (first expr))
    (make-functional-eval env (macroexpand expr)))
   ((assoc (first expr) *map*)
    (make-functional-apply env (cdr (assoc (first expr) *map*)) (rest expr)))
   (t
    (make-apply env (first expr) (rest expr)))))

   
(defmacro functional (&body body)
  (make-functional-eval () `(progn ,@body)))

;;----------------------------------------------------------------------

Exercise to the reader: implement functional SETQ*.
;-)



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Nobody can fix the economy.  Nobody can be trusted with their finger
on the button.  Nobody's perfect.  VOTE FOR NOBODY.
From: Joe Marshall
Subject: Re: `let*' and functional design
Date: 
Message-ID: <r7e9hvli.fsf@comcast.net>
Pascal Bourguignon <···@informatimago.com> writes:

> "Kaz Kylheku" <········@gmail.com> writes:
>> Why should I imagine that there is a tax on the use of LET* which
>> actually reduces how much I have to type, and how deeply I have to
>> nest? Let's measure real costs and consider those to be a tax, rather
>> than imagining taxes that aren't there.
>
> Indeed.
>
>
>> I don't see how you can lump LET* in the same category as RPLACA.
>> That's just weird.
>
> Because they are both as functional as the rest:

[huge amount of code skipped]

You are illustrating the point! (in a really roundabout way, though)

> [172]>  (functional (let ((x (cons 1 2))) 
>                       (print (rplaca x (+ (car x) 1)))
>                       (print (rplaca x (+ (car x) 1)))
>                       (print (rplaca x (+ (car x) 1)))))

Compare the `functional equivalent':

 (let ((x (cons 1 2)))
   (let ((x1 (cons (+ (car x) 1) (cdr x))))
     (print x1)
     (let ((x2 (cons (+ (car x1) 1) (cdr x1))))
       (print x2)
       (let ((x3 (cons (+ (car x2) 1) (cdr x2))))
         (print x3)))))

with the functional expansion of the original:

> (APPLY
>  #'(LAMBDA (#:VENV16542 X)
>     (LET
>      ((#:VENVALS16559
>        (MULTIPLE-VALUE-LIST
>         (APPLY #'RPLACA*
>          (MULTIPLE-VALUE-BIND (#:VENV16560 #:VVAL16561)
>           (VALUES
>            (LET
>             ((#:VENVALS16551
>               (MULTIPLE-VALUE-LIST
>                (APPLY #'RPLACA*
>                 (MULTIPLE-VALUE-BIND (#:VENV16552 #:VVAL16553)
>                  (VALUES
>                   (LET
>                    ((#:VENVALS16543
>                      (MULTIPLE-VALUE-LIST
>                       (APPLY #'RPLACA*
>                        (MULTIPLE-VALUE-BIND (#:VENV16544 #:VVAL16545)
>                         (VALUES #:VENV16542 X)
>                         (LET
>                          ((#:VENVALS16546
>                            (MULTIPLE-VALUE-LIST
>                             (LET
>                              ((#:VENVALS16547
>                                (MULTIPLE-VALUE-BIND (#:VENV16548 #:VVAL16549)
>                                 (APPLY #'CAR*
>                                  (MULTIPLE-VALUE-LIST (VALUES #:VENV16544 X)))
>                                 (LET
>                                  ((#:VENVALS16550
>                                    (MULTIPLE-VALUE-LIST
>                                     (VALUES #:VENV16548 1))))
>                                  (CONS (CAR #:VENVALS16550)
>                                   (CONS #:VVAL16549 (CDR #:VENVALS16550)))))))
>                              (VALUES (CAR #:VENVALS16547)
>                               (APPLY #'+ (CDR #:VENVALS16547)))))))
>                          (CONS (CAR #:VENVALS16546)
>                           (CONS #:VVAL16545 (CDR #:VENVALS16546)))))))))
>                    (VALUES (CAR #:VENVALS16543)
>                     (APPLY #'PRINT (CDR #:VENVALS16543))))
>                   X)
>                  (LET
>                   ((#:VENVALS16554
>                     (MULTIPLE-VALUE-LIST
>                      (LET
>                       ((#:VENVALS16555
>                         (MULTIPLE-VALUE-BIND (#:VENV16556 #:VVAL16557)
>                          (APPLY #'CAR*
>                           (MULTIPLE-VALUE-LIST (VALUES #:VENV16552 X)))
>                          (LET
>                           ((#:VENVALS16558
>                             (MULTIPLE-VALUE-LIST (VALUES #:VENV16556 1))))
>                           (CONS (CAR #:VENVALS16558)
>                            (CONS #:VVAL16557 (CDR #:VENVALS16558)))))))
>                       (VALUES (CAR #:VENVALS16555)
>                        (APPLY #'+ (CDR #:VENVALS16555)))))))
>                   (CONS (CAR #:VENVALS16554)
>                    (CONS #:VVAL16553 (CDR #:VENVALS16554)))))))))
>             (VALUES (CAR #:VENVALS16551) (APPLY #'PRINT (CDR #:VENVALS16551))))
>            X)
>           (LET
>            ((#:VENVALS16562
>              (MULTIPLE-VALUE-LIST
>               (LET
>                ((#:VENVALS16563
>                  (MULTIPLE-VALUE-BIND (#:VENV16564 #:VVAL16565)
>                   (APPLY #'CAR* (MULTIPLE-VALUE-LIST (VALUES #:VENV16560 X)))
>                   (LET
>                    ((#:VENVALS16566
>                      (MULTIPLE-VALUE-LIST (VALUES #:VENV16564 1))))
>                    (CONS (CAR #:VENVALS16566)
>                     (CONS #:VVAL16565 (CDR #:VENVALS16566)))))))
>                (VALUES (CAR #:VENVALS16563)
>                 (APPLY #'+ (CDR #:VENVALS16563)))))))
>            (CONS (CAR #:VENVALS16562)
>             (CONS #:VVAL16561 (CDR #:VENVALS16562)))))))))
>      (VALUES (CAR #:VENVALS16559) (APPLY #'PRINT (CDR #:VENVALS16559)))))
>  (MULTIPLE-VALUE-LIST
>   (APPLY #'CONS*
>    (MULTIPLE-VALUE-BIND (#:VENV16567 #:VVAL16568) (VALUES NIL 1)
>     (LET ((#:VENVALS16569 (MULTIPLE-VALUE-LIST (VALUES #:VENV16567 2))))
>      (CONS (CAR #:VENVALS16569) (CONS #:VVAL16568 (CDR #:VENVALS16569)))))))) ;

What you have shown is that RPLACA is *not* a simple operation.

> So, one could say that functional code is low level, and setq and
> rplaca are high level functional abstractions, if one can judge by the LOC...

Most definitely.  SETQ, RPLACA, and other side-effecting code is
*really hard* to understand because they are so complicated.

-- 
~jrm
From: Matthias Buelow
Subject: Re: `let*' and functional design
Date: 
Message-ID: <86ackxm7de.fsf@drjekyll.mkbuelow.net>
Pascal Bourguignon <···@informatimago.com> writes:

>Because they are both as functional as the rest:
[...]

Of course you can emulate a mutable environment within a functional
language. That follows trivially from the Turing-machine equivalence.
That doesn't make those (emulated) assignments any more functional
than the originals, though. Your rplaca* modifies -within that
`functional' macro environment- the emulated bindings just as rplaca
does outside, so where's the point?

mkb.
From: Pascal Bourguignon
Subject: Re: `let*' and functional design
Date: 
Message-ID: <87y88hm6i2.fsf@thalassa.informatimago.com>
Matthias Buelow <···@incubus.de> writes:

> Pascal Bourguignon <···@informatimago.com> writes:
>
>>Because they are both as functional as the rest:
> [...]
>
> Of course you can emulate a mutable environment within a functional
> language. That follows trivially from the Turing-machine equivalence.
> That doesn't make those (emulated) assignments any more functional
> than the originals, though. Your rplaca* modifies -within that
> `functional' macro environment- the emulated bindings just as rplaca
> does outside, so where's the point?

The point is that when you need a rplaca, you better write it as
(rplaca x newval), or (setf (car x) newval) than try to stuff the
thing artifically into the functional frame.

I use a screedrivers more than once a month, but I use cutting pliers
less than once a year.  Should we tax cutting pliers because they're
non functional (non reversible)?  What happens when we get the quantum
duplicators that will allow us to duplicate first, then cut and
measure after?


Beside, I believe my example is more than a simple Turing Machine
game.  The previous environments are still available.  A continuation
that would have forked before the rplaca would still see and be able
to "modify" the old value, from the old environment.  So I guess
functionalists would still be happy with this "rplaca".


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Litter box not here.
You must have moved it again.
I'll poop in the sink. 
From: Matthias Buelow
Subject: Re: `let*' and functional design
Date: 
Message-ID: <863bqpm4l8.fsf@drjekyll.mkbuelow.net>
Pascal Bourguignon <···@informatimago.com> writes:

>What happens when we get the quantum
>duplicators that will allow us to duplicate first, then cut and
>measure after?

Well, since I believe that the universe is discrete and
nondeterministic in nature, that kind of thing already exists anyways
;-). Apart from that, a bad analogy is just that. :)

mkb.
From: David H. Friedman
Subject: Re: `let*' and functional design
Date: 
Message-ID: <dam48h$3uf$1@reader2.panix.com>
N. Raghavendra <·····@mri.ernet.in> wrote that he read in
Paul Graham's `On Lisp' to be careful using let* "in which
"imperative programs often lie concealed."

N. Raghavendra <·····@mri.ernet.in> wrote:
> This seems to indicate that the use of `let*' is deprecated as far as
> functional programming is concerned.  Why is this so?  Is it common
> practice not to use `let*'?


let* is like let except that bindings can refer to previous bindings, so 
(let ((a 1) (b a) b) errors because a has no value while
(let* ((a 1) (b a) b) evaluates to 1.

I think Graham may be suggesting that the sequencing of bindings is "not 
good functional style" since in functional style order of evaluation 
should not matter.

However, the let* is just another way of writing

((lambda (a) ((lambda (b) b) a)) 1)

(That's lambda-calculus, not lisp) which is perfectly "functional", 
referentially transparent and all that jazz.

So I think Graham may be overstating the case just a bit. Sequencing is 
not inherently non-functional.

But then again, Graham was writing about programming style, not 
programming theory.

-- dhf
From: Kent M Pitman
Subject: Re: `let*' and functional design
Date: 
Message-ID: <u7jg1i70h.fsf@nhplace.com>
"David H. Friedman" <···@panix.com> writes:

> N. Raghavendra <·····@mri.ernet.in> wrote that he read in
> Paul Graham's `On Lisp' to be careful using let* "in which
> "imperative programs often lie concealed."
> 
> N. Raghavendra <·····@mri.ernet.in> wrote:
> > This seems to indicate that the use of `let*' is deprecated as far as
> > functional programming is concerned.  Why is this so?  Is it common
> > practice not to use `let*'?
> 
> 
> let* is like let except that bindings can refer to previous bindings, so 
> (let ((a 1) (b a) b) errors because a has no value while
> (let* ((a 1) (b a) b) evaluates to 1.
> 
> I think Graham may be suggesting that the sequencing of bindings is "not 
> good functional style" since in functional style order of evaluation 
> should not matter.
> 
> However, the let* is just another way of writing
> 
> ((lambda (a) ((lambda (b) b) a)) 1)
> 
> (That's lambda-calculus, not lisp) which is perfectly "functional", 
> referentially transparent and all that jazz.
> 
> So I think Graham may be overstating the case just a bit. Sequencing is 
> not inherently non-functional.

Actually, there's a widespread confusion that somehow LET* introduces
left-to-right evaluation that wasn't there in LET, and this is often
the erroneous reason that LET* is discouraged by people.  I don't know
if Paul had or has this misconception--I would hope not.  But I observe
that even 
 (let ((first-form  (read stream))
       (second-form (read stream)))
   ...)
is very well-defined in its order of evaluation.  I've seen people write
this as 
 (let* ((first-form  (read stream))
        (second-form (read stream)))
   ...)
out of some misguided fear that only this would assure left-to-right (i.e.,
top to bottom) evaluation.  But really Lisp goes to a lot of trouble to
assure left-to-rightness in a lot of places, and LET's initializations
is one of these.

> But then again, Graham was writing about programming style, not 
> programming theory.

Hmmm.  Can you elaborate on this distinction?  (I'm not challenging it,
I just don't hear the term "programming theory" much, especially in so 
precise a way as to talk about it as making a distinction between it and
something else ... I was curious what it means [to you].)
From: Mike
Subject: Re: `let*' and functional design
Date: 
Message-ID: <qtzze.33126$B_3.31047@fe05.lga>
On 2005-07-08, Kent M Pitman <······@nhplace.com> wrote:
> "David H. Friedman" <···@panix.com> writes:
>
>> N. Raghavendra <·····@mri.ernet.in> wrote that he read in
>> Paul Graham's `On Lisp' to be careful using let* "in which
>> "imperative programs often lie concealed."
>> 
>> N. Raghavendra <·····@mri.ernet.in> wrote:
>> > This seems to indicate that the use of `let*' is deprecated as far as
>> > functional programming is concerned.  Why is this so?  Is it common
>> > practice not to use `let*'?
>> 
>> 
>> let* is like let except that bindings can refer to previous bindings, so 
>> (let ((a 1) (b a) b) errors because a has no value while
>> (let* ((a 1) (b a) b) evaluates to 1.
>> 
>> I think Graham may be suggesting that the sequencing of bindings is "not 
>> good functional style" since in functional style order of evaluation 
>> should not matter.
>> 
>> However, the let* is just another way of writing
>> 
>> ((lambda (a) ((lambda (b) b) a)) 1)
>> 
>> (That's lambda-calculus, not lisp) which is perfectly "functional", 
>> referentially transparent and all that jazz.
>> 
>> So I think Graham may be overstating the case just a bit. Sequencing is 
>> not inherently non-functional.
>
> Actually, there's a widespread confusion that somehow LET* introduces
> left-to-right evaluation that wasn't there in LET, and this is often
> the erroneous reason that LET* is discouraged by people.  I don't know
> if Paul had or has this misconception--I would hope not.  But I observe
> that even 
>  (let ((first-form  (read stream))
>        (second-form (read stream)))
>    ...)
> is very well-defined in its order of evaluation.  I've seen people write
> this as 
>  (let* ((first-form  (read stream))
>         (second-form (read stream)))
>    ...)
> out of some misguided fear that only this would assure left-to-right (i.e.,
> top to bottom) evaluation.  But really Lisp goes to a lot of trouble to
> assure left-to-rightness in a lot of places, and LET's initializations
> is one of these.
>
>> But then again, Graham was writing about programming style, not 
>> programming theory.
>
> Hmmm.  Can you elaborate on this distinction?  (I'm not challenging it,
> I just don't hear the term "programming theory" much, especially in so 
> precise a way as to talk about it as making a distinction between it and
> something else ... I was curious what it means [to you].)

What I use let* for and am confused about (is there a better way), is
writing a defun to do something like open a socket connection and send
some data. This is just typing and not tested. I'm also learning CL, so
my code is apt to be bad.

(defun doconnect (host port)
  "Connect to a remote socket."
  (let* ((addr (get-host-address host))
         (num (host-to-net-short port))
		 (sock (open-socket-connection addr num)))
    (format sock "~a~%" "Hello World")))

None of the functions exist, again I'm just typing. I'm trying to
demonstrate how I've tried to code things and why I use let*. Are there
better ways to do this? I know I could set 'sock' to nil in a 'let' then
do the open-socket-connection within the (let ...). That way I'm not
dependent on the order of evaluation between addr, num, and sock.

Mike
From: Joe Marshall
Subject: Re: `let*' and functional design
Date: 
Message-ID: <4qb5jdlr.fsf@comcast.net>
Mike <·····@mikee.ath.cx> writes:

> What I use let* for and am confused about (is there a better way), is
> writing a defun to do something like open a socket connection and send
> some data. This is just typing and not tested. I'm also learning CL, so
> my code is apt to be bad.
>
> (defun doconnect (host port)
>   "Connect to a remote socket."
>   (let* ((addr (get-host-address host))
>          (num (host-to-net-short port))
>          (sock (open-socket-connection addr num)))
>     (format sock "~a~%" "Hello World")))

> None of the functions exist, again I'm just typing.

Let's use this as a hypothetical.

I'd probably write it more like this:

(defun doconnect (host port)
  "Connect to a remote socket."
  (let ((sock (open-socket-connection
                (get-host-address host)
                (host-to-net-short port))))
    (format sock "~a~%" "Hello World")))

I wouldn't bother creating a binding for ADDR and NUM because they are
only used in one place and the dataflow is more obvious when the
expressions are right where they are being used.

This argument also applies to SOCK, but then the form becomes this:

(defun doconnect (host port)
  "Connect to a remote socket."
  (format (open-socket-connection
            (get-host-address host)
            (host-to-net-short port))
          "~a~%"
          "Hello World"))

The format string is just seems too far from the FORMAT identifier.

> I'm trying to demonstrate how I've tried to code things and why I
> use let*.  Are there better ways to do this?  I know I could set
> 'sock' to nil in a 'let' then do the open-socket-connection within
> the (let ...).  That way I'm not dependent on the order of evaluation
> between addr, num, and sock.

The problem with LET* is that it can lure you into a `step-by-step'
programming style.  Not that there is anything wrong with a
step-by-step style if you are solving a step-by-step problem, but if
you are solving a problem by combining solutions to subproblems, then
your code should illustrate where the subproblems fit into the
combined solution.



-- 
~jrm
From: Kent M Pitman
Subject: Re: `let*' and functional design
Date: 
Message-ID: <ubr5d2izt.fsf@nhplace.com>
Mike <·····@mikee.ath.cx> writes:

> What I use let* for and am confused about (is there a better way), is
> writing a defun to do something like open a socket connection and send
> some data. This is just typing and not tested. I'm also learning CL, so
> my code is apt to be bad.
> 
> (defun doconnect (host port)
>   "Connect to a remote socket."
>   (let* ((addr (get-host-address host))
>          (num (host-to-net-short port))
>          (sock (open-socket-connection addr num)))
>     (format sock "~a~%" "Hello World")))

[Don't put tabs in news posts or the indentation will do weird stuff when
 it's prefixed by ">" and the mod8 boundaries change, not to mention the
 parts of the world that don't use 8-wide tabs.]

> None of the functions exist, again I'm just typing. I'm trying to
> demonstrate how I've tried to code things and why I use let*. Are there
> better ways to do this? I know I could set 'sock' to nil in a 'let' then
> do the open-socket-connection within the (let ...). That way I'm not
> dependent on the order of evaluation between addr, num, and sock.

I don't see why you worry.  What you're doing above is pretty much exactly
what LET* is intended for.

The most common style rule says to use LET unless you need to have some of
the variables depend on the value of others, then use LET*.  There are 
purists who would have you write:

 (defun doconnect (host port)
   "Connect to a remote socket."
   (let ((addr (get-host-address host))
         (num (host-to-net-short port)))
     (let ((sock (open-socket-connection addr num)))
       (format sock "~a~%" "Hello World"))))

But this increases indentation level ad I don't know that it's hugely
more readable than the LET* version.

By the way, there is a minority viewpoint taught to me by Dick Waters
(architect of the CL pretty printer and of the ITERATE stuff in CLTL2,
among other things).  I don't find his programming style very readable
personally, but it's not because he doesn't know what he's doing.  I
think it's just because he has a different way of thinking than most
people.  It's thoughtful, but diverges from many standard practices.
Anyway, he says you should use LET* rather than LET by default because
it's left-to-right anyway, and you should only use LET to alert people
that you're trying to rely on the outer context.  That is, if you do:
 (LET ((X ...) (Y ...)) ...)
he would have you be alarmed that Y depends on X's outer value, and
ordinarily you should just assume that variable bindings are automatically 
instantiated as they are seen.  Before you think this is totally hokey,
I will add that he offers some pretty interesting reasons.  One is that
LET* is cheaper to implement--that is, a straightforward implementation 
has it compute a value and then install the value in the variable.  
In LET's case, it actually has to compute all the values in temps, keeping
the original variables available until the end, and then can finally collapse
things.  (Naturally good code analysis can collapse this out, but he's
talking partly interpreters, and to some extent he's making a vague 
cognitive argument that the human mind is a kind of interpreter that has
to hold more state when it reads a LET than when it reads a LET*.)  He
further observes that the choice of naming of LET and LET* is arbitrary
and that the main reason people prefer LET is probably that it has the
better name.  But finally, he also observes that bindings in a DEFUN work
left to right (as you note if you use optionals and you see the values
filled left to right) and it's somewhat more linguistically consistent
to think of a LET* list and an &AUX list as essentially the same, while
LET is something wholly different.   I'm pretty sure he'd write even:
 (let* ((x 3)) (+ x x))
which is something most people would not even THINK of doing.
(And I think he prefers DO* to DO.)

This all points to the fact that rules of style are not absolutes.  They
are theories of consistency, but that to read someone else's code, you need
to know what they think is normal and what they think requires flagging,
since different people--even very smart people--can think differently 
about things.
From: Joe Marshall
Subject: Re: `let*' and functional design
Date: 
Message-ID: <vf3lhwde.fsf@comcast.net>
Kent M Pitman <······@nhplace.com> writes:

> By the way, there is a minority viewpoint taught to me by Dick Waters
> (architect of the CL pretty printer and of the ITERATE stuff in CLTL2,
> among other things).  

Dick Waters is an accomplished and well respected Lisp hacker.

> Anyway, he says you should use LET* rather than LET by default because
> it's left-to-right anyway, and you should only use LET to alert people
> that you're trying to rely on the outer context.  That is, if you do:
>  (LET ((X ...) (Y ...)) ...)
> he would have you be alarmed that Y depends on X's outer value, and
> ordinarily you should just assume that variable bindings are automatically 
> instantiated as they are seen.  

In the more common usage, LET is used to indicate that there are no
dependencies amongst the variables being bound, and LET* to indicate
that there are.

When I see
   (LET ((X ...)
         (Y ...))
     ...)

I have no reason to believe that Y depends on X's `outer value', I
just know it doesn't depend on that X right above it.  I get alarmed
when I see

    (let* ((p ...)
           (q ...)
           (r ...)
           (s ...))
      ...)

Because I expect each computation to depend on some or all of the
prior ones.

Of course, if you followed Dick's reasoning and style, you would only
use the `normal' LET if you actually had a name collision, and since
most of code would use LET*, there'd be no particular expectation of
dependencies.

I still think it is hokey, though.  Here is why:  If I were concerned
about Y depending on an `outer value' of X, it is trivial to solve by
alpha-renaming the inner X to something else.  In Dick's style, there
is no reason to ever use LET in favor of LET*.  On the other hand,
when confronted with a LET* in Dick's style, like this one,

    (let* ((p ...)
           (q ...)
           (r ...)
           (s ...))
      ...)

I can't figure out the dependency chain without examining the code for
occurrances of the identifiers.  This is important because you cannot
refactor and rearrange code without understanding the dependencies.

> This all points to the fact that rules of style are not absolutes.  They
> are theories of consistency, but that to read someone else's code, you need
> to know what they think is normal and what they think requires flagging,
> since different people--even very smart people--can think differently 
> about things.

You shouldn't `buck the system' unless you have a good reason to do
so, though.

-- 
~jrm
From: Kent M Pitman
Subject: Re: `let*' and functional design
Date: 
Message-ID: <uvf3kncb3.fsf@nhplace.com>
Joe Marshall <·············@comcast.net> writes:

> Kent M Pitman <······@nhplace.com> writes:
> [...]
> > This all points to the fact that rules of style are not absolutes.  They
> > are theories of consistency, but that to read someone else's code, you need
> > to know what they think is normal and what they think requires flagging,
> > since different people--even very smart people--can think differently 
> > about things.
> 
> You shouldn't `buck the system' unless you have a good reason to do
> so, though.

Right.  But my point is that there is, a priori, no uniquely determined
"system".  The common style rule is, as you say, to use LET.  But in part
that's because there were at one time more of people like you and me than
like Dick Waters.  On the other hand, he is no dummy.  I daresay the
world could use more like him.  And if there were, perhaps the "system"
would be different.

But like everything else in Common Lisp, the value often comes not from
the notion of "this was the best you could get" but rather from "there's
a cost just to ongoing debate, and there's value in just pushing ahead".
If that's what you mean by "the system" that people should not buck, I 
guess I go along with that.

I just like that it is yet another datum that confirms my often-used
meta-point--that wisdom in what to do is not about doing something a
certain way, but about thinking hard and coming up with good ways to do
things.  Wise people sometimes totally disagree.  But they also often 
know how to just make an arbitrary decision and move ahead rather than
haggling forever.

Most style rules are of this kind.  There are multiple ways you can
go.  It's good to have a preferred way.  Sometimes that preferred way
is just arbitrary.  And that's ok.  Because at least if everyone goes
the same arbitrary way, there's still useful info to be had in
observing that someone deviated from the norm.  If everyone reasons from
first principles every time, there's no info to be had in that same
situation.
From: Bruce Hoult
Subject: Re: `let*' and functional design
Date: 
Message-ID: <bruce-672BA1.10513509072005@news.clear.net.nz>
In article <············@comcast.net>,
 Joe Marshall <·············@comcast.net> wrote:

> I still think it is hokey, though.  Here is why:  If I were concerned
> about Y depending on an `outer value' of X, it is trivial to solve by
> alpha-renaming the inner X to something else.  In Dick's style, there
> is no reason to ever use LET in favor of LET*.  On the other hand,
> when confronted with a LET* in Dick's style, like this one,
> 
>     (let* ((p ...)
>            (q ...)
>            (r ...)
>            (s ...))
>       ...)
> 
> I can't figure out the dependency chain without examining the code for
> occurrances of the identifiers.  This is important because you cannot
> refactor and rearrange code without understanding the dependencies.

I really hate the CL/Scheme "let" because I like to give meaningful 
names to lots of intermediate values and doing that using "let" quickly 
gets you on the right hand side of the screen, for no benefit.  So I use 
"let*" almost by reflex anywhere I have a body.

In functional code, even if each name is then only used once the 
dependencies are exactly the same as if its definition had been inserted 
into the point of use.  If it is used more than once then you've got a 
savings in both time and human understanding in factoring out a common 
subexpression.


I also use let* as the contents of many or most bodies in imperative 
code.  But it's very easy to tell the difference -- in imperative code 
the expression bound to some of the names is a progn (begin in Scheme) 
containing all the side-effecting code that needs to be executed between 
the previous binding (and presumably using it) and this one.

e.g.

(let* ((name (begin (printf "What is your name?: ")
                    (read)))
       (age (begin (printf "Hi, ~a, how old are you?: " name)
                   (read))))
  (printf "Thanks for the information~n")
  age)


I find this quite ugly, and a little bit backwards, compared to how 
you'd do this in Dylan (or C++ or Java or Perl or ...) but I can't see a 
better alternative in CL/Scheme.

begin
   format-out("What is your name?: ");
   let name = read-line();
   format-out("Hi, %s, how old are you?: ", name);
   let age = read-line();
   format-out("Thanks for the information\n")
   age;
end

-- 
Bruce |  41.1670S | \  spoken |          -+-
Hoult | 174.8263E | /\ here.  | ----------O----------
From: Don Geddis
Subject: Re: `let*' and functional design
Date: 
Message-ID: <87r7e8mykq.fsf@sidious.geddis.org>
Bruce Hoult <·····@hoult.org> wrote on Sat, 09 Jul 2005:
> (let* ((name (begin (printf "What is your name?: ")
>                     (read)))
>        (age (begin (printf "Hi, ~a, how old are you?: " name)
>                    (read))))
>   (printf "Thanks for the information~n")
>   age)
> I find this quite ugly, and a little bit backwards, compared to how 
> you'd do this in Dylan (or C++ or Java or Perl or ...) but I can't see a 
> better alternative in CL/Scheme.
> begin
>    format-out("What is your name?: ");
>    let name = read-line();
>    format-out("Hi, %s, how old are you?: ", name);
>    let age = read-line();
>    format-out("Thanks for the information\n")
>    age;
> end

I'd probably code this in CL as

(let (name age)
  (format t "What is your name?: ")
  (setq name (read-line))
  (format t "Hi ~A, how old are you?: " name)
  (setq age (read-line))
  (format t "Thanks for the information~%")
  age )

That strikes me as pretty much the same feel as your Dylan example, no?

        -- Don
_______________________________________________________________________________
Don Geddis                  http://don.geddis.org/               ···@geddis.org
It takes a big man to cry, but it takes a bigger man to laugh at that man.
	-- Deep Thoughts, by Jack Handey
From: Bruce Hoult
Subject: Re: `let*' and functional design
Date: 
Message-ID: <bruce-A9B721.20514109072005@news.clear.net.nz>
In article <··············@sidious.geddis.org>,
 Don Geddis <···@geddis.org> wrote:

> I'd probably code this in CL as
> 
> (let (name age)
>   (format t "What is your name?: ")
>   (setq name (read-line))
>   (format t "Hi ~A, how old are you?: " name)
>   (setq age (read-line))
>   (format t "Thanks for the information~%")
>   age )
> 
> That strikes me as pretty much the same feel as your Dylan example, no?

That's pragmatic, and how things are programmed in C and Pascal, but I 
really like the declare-at-point-of-initialization meme that all recent 
(mid 80's or newer) languages I can think of have.

-- 
Bruce |  41.1670S | \  spoken |          -+-
Hoult | 174.8263E | /\ here.  | ----------O----------
From: Kent M Pitman
Subject: Re: `let*' and functional design
Date: 
Message-ID: <ur7e8n9nm.fsf@nhplace.com>
Bruce Hoult <·····@hoult.org> writes:

> In article <············@comcast.net>,
>  Joe Marshall <·············@comcast.net> wrote:
> 
> > I still think it is hokey, though.  Here is why:  If I were concerned
> > about Y depending on an `outer value' of X, it is trivial to solve by
> > alpha-renaming the inner X to something else.  In Dick's style, there
> > is no reason to ever use LET in favor of LET*.  On the other hand,
> > when confronted with a LET* in Dick's style, like this one,
> > 
> >     (let* ((p ...)
> >            (q ...)
> >            (r ...)
> >            (s ...))
> >       ...)
> > 
> > I can't figure out the dependency chain without examining the code for
> > occurrances of the identifiers.  This is important because you cannot
> > refactor and rearrange code without understanding the dependencies.
> 
> I really hate the CL/Scheme "let" because I like to give meaningful 
> names to lots of intermediate values and doing that using "let" quickly 
> gets you on the right hand side of the screen, for no benefit.  So I use 
> "let*" almost by reflex anywhere I have a body.
> 
> In functional code, even if each name is then only used once the 
> dependencies are exactly the same as if its definition had been inserted 
> into the point of use.  If it is used more than once then you've got a 
> savings in both time and human understanding in factoring out a common 
> subexpression.
> 
> 
> I also use let* as the contents of many or most bodies in imperative 
> code.  But it's very easy to tell the difference -- in imperative code 
> the expression bound to some of the names is a progn (begin in Scheme) 
> containing all the side-effecting code that needs to be executed between 
> the previous binding (and presumably using it) and this one.
> 
> e.g.
> 
> (let* ((name (begin (printf "What is your name?: ")
>                     (read)))
>        (age (begin (printf "Hi, ~a, how old are you?: " name)
>                    (read))))
>   (printf "Thanks for the information~n")
>   age)
> 
> 
> I find this quite ugly, and a little bit backwards, compared to how 
> you'd do this in Dylan (or C++ or Java or Perl or ...) but I can't see a 
> better alternative in CL/Scheme.
> 
> begin
>    format-out("What is your name?: ");
>    let name = read-line();
>    format-out("Hi, %s, how old are you?: ", name);
>    let age = read-line();
>    format-out("Thanks for the information\n")
>    age;
> end

Historically, the reason this is not done in CL is not that people
didn't want it, it's because the scope of the variable is messy.

I seem to recall that Sussman experimented with this in MIT Scheme for
a while--I'm not sure if it's still there, or if they backed out of
it.  He had the notion of a hard boundary at certain levels of the
program that could be affected by the execution of certain kinds of
local setqs.  This allowed you to do things like define within define,
and something called lsetq which was different than setq in that it
assigned "locally", which I _think_ meant at the contour of the
innermost let.  (I was alleging at the time was problematic because if
a macro expanded into a let, you wouldn't know where that was.  But
this may have been fixed by the macro hygiene stuff, which was new and
experimental at the time such discussions were ongoing.)

As I recall, there were also some design issues as to whether you could
do things like
  (let ((foo 3))
    (dotimes (i 5)
      (if (oddp i) (lsetq foo i))
      (print foo)))
and did this mean 
 * lsetq affects only the if [the C-ish answer]
 * lsetq just notices the outer foo and uses it,
   meaning all references to foo there use that
 * lsetq makes a new foo somewhere between the outer foo and
   the lsetq and that foo might or might not affect the (print foo)'s foo.
These are the kinds of things that programmers see as "already nailed down".
Language design is the process of making sure these are not issues of 
speculation, but things with both well-defined answers and answers that 
"make sense" and "feel good" and "are useful" in the context of real programs.
I don't recall what the Scheme answers were, but this was the approximate 
shape of the design space for this linguistic element at one point in
Scheme's history as I remember it.

[Aside: Probably they called it lset! not lsetq, of course. I just
translate those in my head to avoid the visual pain of seeing "lset!".
I find it inappropriate to talk about setq-like behavior as a
side-effect since it affects no Lisp datastructure, it only affects
the interpreter's/compiler's data structures, so would be a
side-effect in a 3-lisp reflective tower, but not in the current level
of the tower, always in a level one off from that.]

In Common Lisp, we had this issue with TAGBODY and BLOCK where where
you have to know where the tagbodies and blocks are for GO and RETURN
to really work.  If someone slips in an extra one that's visible to
your construct, you're sort of screwed.  So you really have to
document any time you put in a NIL block, and otherwise you're best
off to stick to actually or effectively gensymed names.  (Sometimes
you can prove that a symbol that's interned will suffice in place of a
gensym, but in the general case where you can't, a gensym is safer.)

In T (Yale Scheme), we took a kind of intermediate approach, creating
something called a LOCALE (a construct I invented to address the
problem that Jonathan Rees hated the notion of "toplevel" because it
was semantically ill-defined, yet I wanted to allow the expressive
power that it offered.  A locale was a kind of "local toplevel" that
had a clearer boundary than a regular toplevel.  Source file toplevels
corresponded implicitly to locales, but it was possible to create them
in other places using an expressional form as well, so the notion of
"file" was not necessary to explaining T's scoping rules.

Of course, when dylan went infix, one of the things it did was to
focus on allowing assignments in what amounted to a "block" of
code. (Sort of like a LOCALE, but maybe not as heavyweight.) Another
way Lisp could have gone was to make something that was not LET and
not PROGN but was a proper blockish thing to which assignments were
made.  A lot of time, it would be painful to see, I think.  And
documenting the set of places where it was rather than PROGN would
have been a pain.  One could have just decided that PROGN should just
always be BLOCK, but that would remove the ability to introduce a
sequence of commands that did not introduce a binding level, and that
could be problematic in its own regard.

So it's a hard problem. But I wouldn't want you to think it wasn't
thought about.  Large numbers of hours were spent by lots of people
thinking out the entire design space in several languages/dialects,
with interestingly different results.

I've often said that languages are like ecosystems.  What's good and
bad depends a lot not just on the operator, but on the surrounding
operations, the set of users, etc.  This is a good example of that.
From: Brian Downing
Subject: Re: `let*' and functional design
Date: 
Message-ID: <UaKze.148209$xm3.68607@attbi_s21>
In article <···························@news.clear.net.nz>,
Bruce Hoult  <·····@hoult.org> wrote:
> (let* ((name (begin (printf "What is your name?: ")
>                     (read)))
>        (age (begin (printf "Hi, ~a, how old are you?: " name)
>                    (read))))
>   (printf "Thanks for the information~n")
>   age)
> 
> I find this quite ugly, and a little bit backwards, compared to how 
> you'd do this in Dylan (or C++ or Java or Perl or ...) but I can't see a 
> better alternative in CL/Scheme.
> 
> begin
>    format-out("What is your name?: ");
>    let name = read-line();
>    format-out("Hi, %s, how old are you?: ", name);
>    let age = read-line();
>    format-out("Thanks for the information\n")
>    age;
> end

If you really want it you can always do it yourself:

(defmacro crazy-scope (&body body)
  "5 minute marginal hack"
  (let (leftovers)
    `(progn
       ,@(loop for form on body
               while (not (eql (car form) 'let))
               collect (car form)
               finally (setf leftovers form))
       ,@(if (and leftovers (cdr leftovers))
             `((let ((,(second leftovers) ,(fourth leftovers)))
                 (crazy-scope ,@(cddddr leftovers))))
             leftovers))))

CL-USER> (crazy-scope
           (format t "What is your name?: ")
           let name = (read-line)
           (format t "Hi, ~A, how old are you?: " name)
           let age = (read-line)
           (format t "Thanks for the information.~%")
           age)
What is your name?: Brian
Hi, Brian, how old are you?: 3
Thanks for the information.
"3"

-bcd
-- 
*** Brian Downing <bdowning at lavos dot net> 
From: Bruce Hoult
Subject: Re: `let*' and functional design
Date: 
Message-ID: <bruce-61100F.20490509072005@news.clear.net.nz>
In article <······················@attbi_s21>,
 Brian Downing <·············@lavos.net> wrote:

> If you really want it you can always do it yourself:
> 
> (defmacro crazy-scope (&body body)

Yep, and in fact a couple of years ago I wrote just such a macro. :-)

-- 
Bruce |  41.1670S | \  spoken |          -+-
Hoult | 174.8263E | /\ here.  | ----------O----------
From: Joe Marshall
Subject: Re: `let*' and functional design
Date: 
Message-ID: <pstrhjei.fsf@comcast.net>
Bruce Hoult <·····@hoult.org> writes:

> I really hate the CL/Scheme "let" because I like to give meaningful 
> names to lots of intermediate values

I try to avoid giving names to intermediate values.  I'll make an
intermediate variable only if

   1.  The value is used in more than one place.

   2.  The intermediate value is `important enough' to deserve a name.

   3.  Using the value producing form `in-place' clutters the code. 

> I also use let* as the contents of many or most bodies in imperative 
> code.  But it's very easy to tell the difference -- in imperative code 
> the expression bound to some of the names is a progn (begin in Scheme) 
> containing all the side-effecting code that needs to be executed between 
> the previous binding (and presumably using it) and this one.
>
> e.g.
>
> (let* ((name (begin (printf "What is your name?: ")
>                     (read)))
>        (age (begin (printf "Hi, ~a, how old are you?: " name)
>                    (read))))
>   (printf "Thanks for the information~n")
>   age)

For this, I'd write a prompting-read subroutine.

-- 
~jrm
From: Raffael Cavallaro
Subject: Re: `let*' and functional design
Date: 
Message-ID: <2005070823475575249%raffaelcavallaro@pasdespamsilvousplaitmaccom>
On 2005-07-08 15:29:44 -0400, Kent M Pitman <······@nhplace.com> said:

> I'm pretty sure he'd write even:
>  (let* ((x 3)) (+ x x))
> which is something most people would not even THINK of doing.

I've noticed that Gary Byers of MCL/OpenMCL fame seems to use let* by 
default as well.
From: Alan Crowe
Subject: Re: `let*' and functional design
Date: 
Message-ID: <86u0j5136g.fsf@cawtech.freeserve.co.uk>
Mike <·····@mikee.ath.cx> wrote:

     (defun doconnect (host port)
       "Connect to a remote socket."
       (let* ((addr (get-host-address host))
	      (num (host-to-net-short port))
		      (sock (open-socket-connection addr num)))
	 (format sock "~a~%" "Hello World")))

     None of the functions exist, again I'm just typing. I'm trying to
     demonstrate how I've tried to code things and why I use let*. Are there
     better ways to do this? I know I could set 'sock' to nil in a 'let' then
     do the open-socket-connection within the (let ...). That way I'm not
     dependent on the order of evaluation between addr, num, and sock.

The issue is not "order of evaluation" but visibility of
bindings.

Consider 
* (let ((x 'outer))
    (let ((x (print 'inner))
	  (y (print (list 'x x))))))
INNER 
(X OUTER) 
NIL

* (let ((x 'outer))
    (let* ((x (print 'inner))
	  (y (print (list 'x x))))))
INNER 
(X INNER) 
NIL

Clearly the initialisation form for the inner x got
evaluated first in both cases, causing the symbol to be
printed before the list.

The difference is that the initialisation form for y in let
sees the old binding of x.

Remember that let and lambda are really the same. So we can
rewrite the inner let as a lambda

* (let ((x 'outer))
    ((lambda (x y)"Burke and Hare were here!")
     (print 'inner)
     (print (list 'x x))))
INNER 
(X OUTER) 
"Burke and Hare were here!"

Now it seems natural that (print 'inner) is evaluated before 
(print (list 'x x)) by the usual left to right evaluation of
argument lists, and it also seems natural that (list 'x x)
should pick up the outer x, with the inner binding of x only
being visible in the (snatched) body of lambda.

The equivalence of LET and LAMBDA tells us how let must
behave. Sometimes we wish LET behaved differently. For those
times there is LET*

Alan Crowe
Edinburgh
Scotland
From: Pascal Bourguignon
Subject: Re: `let*' and functional design
Date: 
Message-ID: <87ackxnlvl.fsf@thalassa.informatimago.com>
Mike <·····@mikee.ath.cx> writes:
> What I use let* for and am confused about (is there a better way), is
> writing a defun to do something like open a socket connection and send
> some data. This is just typing and not tested. I'm also learning CL, so
> my code is apt to be bad.
>
> (defun doconnect (host port)
>   "Connect to a remote socket."
>   (let* ((addr (get-host-address host))
>          (num (host-to-net-short port))
> 		 (sock (open-socket-connection addr num)))
>     (format sock "~a~%" "Hello World")))
>
> None of the functions exist, again I'm just typing. I'm trying to
> demonstrate how I've tried to code things and why I use let*. Are there
> better ways to do this? I know I could set 'sock' to nil in a 'let' then
> do the open-socket-connection within the (let ...). That way I'm not
> dependent on the order of evaluation between addr, num, and sock.

The functionnal terrorists would want you to write it as:

(defun doconnect (host port)
   "Connect to a remote socket."
   (format
       (open-socket-connection
          (get-host-address host)
          (host-to-net-short port))
       "~a~%" "Hello World"))

but lisp pragmatics know that it's all Turing Machines down under and
that it's better to let the computer do the macro stuff, and both forms
compile to basically the same thing:


[13]> (disassemble (compile (defun doconnect (host port)
   "Connect to a remote socket."
   (let* ((addr (get-host-address host))
          (num (host-to-net-short port))
 		 (sock (open-socket-connection addr num)))
     (format sock "~a~%" "Hello World")))))
WARNING in DOCONNECT :
Function GET-HOST-ADDRESS is not defined
WARNING in DOCONNECT :
Function HOST-TO-NET-SHORT is not defined
WARNING in DOCONNECT :
Function OPEN-SOCKET-CONNECTION is not defined

Disassembly of function DOCONNECT
(CONST 0) = GET-HOST-ADDRESS
(CONST 1) = HOST-TO-NET-SHORT
(CONST 2) = OPEN-SOCKET-CONNECTION
(CONST 3) = #<COMPILED-FUNCTION DOCONNECT-1>
(CONST 4) = "Hello World"
(CONST 5) = FORMAT
2 required arguments
0 optional arguments
No rest parameter
No keyword parameters
12 byte-code instructions:
0     (LOAD&PUSH 2)
1     (CALL1&PUSH 0)                      ; GET-HOST-ADDRESS
3     (LOAD&PUSH 2)
4     (CALL1&PUSH 1)                      ; HOST-TO-NET-SHORT
6     (LOAD&PUSH 1)
7     (LOAD&PUSH 1)
8     (CALL2&PUSH 2)                      ; OPEN-SOCKET-CONNECTION
10    (LOAD&PUSH 0)
11    (CONST&PUSH 3)                      ; #<COMPILED-FUNCTION DOCONNECT-1>
12    (CONST&PUSH 4)                      ; "Hello World"
13    (CALL 3 5)                          ; FORMAT
16    (SKIP&RET 6)
NIL
[14]> (disassemble (compile (defun doconnect (host port)
   "Connect to a remote socket."
   (format
       (open-socket-connection
          (get-host-address host)
          (host-to-net-short port))
       "~a~%" "Hello World"))))
WARNING in DOCONNECT :
Function OPEN-SOCKET-CONNECTION is not defined
WARNING in DOCONNECT :
Function GET-HOST-ADDRESS is not defined
WARNING in DOCONNECT :
Function HOST-TO-NET-SHORT is not defined

Disassembly of function DOCONNECT
(CONST 0) = GET-HOST-ADDRESS
(CONST 1) = HOST-TO-NET-SHORT
(CONST 2) = OPEN-SOCKET-CONNECTION
(CONST 3) = #<COMPILED-FUNCTION DOCONNECT-1>
(CONST 4) = "Hello World"
(CONST 5) = FORMAT
2 required arguments
0 optional arguments
No rest parameter
No keyword parameters
9 byte-code instructions:
0     (LOAD&PUSH 2)
1     (CALL1&PUSH 0)                      ; GET-HOST-ADDRESS
3     (LOAD&PUSH 2)
4     (CALL1&PUSH 1)                      ; HOST-TO-NET-SHORT
6     (CALL2&PUSH 2)                      ; OPEN-SOCKET-CONNECTION
8     (CONST&PUSH 3)                      ; #<COMPILED-FUNCTION DOCONNECT-1>
9     (CONST&PUSH 4)                      ; "Hello World"
10    (CALL 3 5)                          ; FORMAT
13    (SKIP&RET 3)
NIL
[15]> 

The only difference here being the duplication of the arguments to
open-socket-connection on the stack for the optimizer of clisp is
rather lazy.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Nobody can fix the economy.  Nobody can be trusted with their finger
on the button.  Nobody's perfect.  VOTE FOR NOBODY.
From: Hannah Schroeter
Subject: Re: `let*' and functional design
Date: 
Message-ID: <dauaae$njq$1@c3po.use.schlund.de>
Hello!

David H. Friedman <···@panix.com> wrote:
>N. Raghavendra <·····@mri.ernet.in> wrote that he read in
>Paul Graham's `On Lisp' to be careful using let* "in which
>"imperative programs often lie concealed."

>[...]

>I think Graham may be suggesting that the sequencing of bindings is "not 
>good functional style" since in functional style order of evaluation 
>should not matter.

Which is not entirely true. In pure functional programming,
the order of evaluation *can* be restricted, but only through
data dependencies.

And that's one point for which I might prefer let* over let, in fact:

(let* ((a some-common-subexpression)
       (b (function-1 some-common-subexpression)
       (c (function-2 some-common-subexpression)
       ...)
  ... body using b and c, perhaps also a directly)

>[...]

>But then again, Graham was writing about programming style, not 
>programming theory.

Yeah, but I think extracting common subexpressions is actually
*good* style.

>-- dhf

Kind regards,

Hannah.
From: Pascal Bourguignon
Subject: Re: `let*' and functional design
Date: 
Message-ID: <874qb5pg6p.fsf@thalassa.informatimago.com>
"N. Raghavendra" <·····@mri.ernet.in> writes:

> I was reading Paul Graham's book `On Lisp', and found the following
> passage in Chapter 3, `Functional Programming':
>
>   "Having functional programming as an ideal doesn't imply that
>    programs should never have side-effects. It just means that they
>    should have no more than necessary.
>
>    "It may take time to develop this habit.  One way to start is to
>    treat the following operators as if there were a tax on their use:
>    set setq setf psetf psetq incf decf push pop pushnew rplaca rplacd
>    rotatef shiftf remf remprop remhash and also let*, in which
>    imperative programs often lie concealed. Treating these operators
>    as taxable is only proposed as a help toward, not a criterion for,
>    good Lisp style.  However, this alone can get you surprisingly
>    far."
>
> This seems to indicate that the use of `let*' is deprecated as far as
> functional programming is concerned.  Why is this so?  Is it common
> practice not to use `let*'?


http://home.pipeline.com/~hbaker1/MetaCircular.html

(shadow '(let let*))

(defmacro let (vs &body forms)
  `((lambda ,(mapcar #'car vs) ,@forms) ,@(mapcar #'cadr vs)))

(defmacro let* (vs &body forms)
  (if vs
    `(let (,(car vs)) (let* ,(cdr vs) ,@forms))
    `(let () ,@forms)))


[9]> (tree-delete 'declare (ext:expand-form 
        '(let* ((a 3) (b (1+ a)) (c (* a b))) (print c))) 
      :key (lambda (node) (if (consp node) (car node) node)))

((LAMBDA (A)
  ((LAMBDA (B) ((LAMBDA (C) ((LAMBDA NIL (PRINT C)))) (* A B))) (1+ A)))
  3)

[10]> ((LAMBDA (A)
  ((LAMBDA (B) ((LAMBDA (C) ((LAMBDA NIL (PRINT C)))) (* A B))) (1+ A)))
  3)

12 

Where do you see a LET or a LET* ?

In practice, you use whatever you want to express your algorithms readably.


We could even define macros to replace all assignments with the
functional construction of a new environment, so rplaca et al. are not
even anti functional.  And remember, the world IS non functional: when
you break a tea cup, you can't keep a pointer to the unbroken tea cup!


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
In deep sleep hear sound,
Cat vomit hairball somewhere.
Will find in morning.
From: Matthias Buelow
Subject: Re: `let*' and functional design
Date: 
Message-ID: <86ackxh0nn.fsf@drjekyll.mkbuelow.net>
Pascal Bourguignon <···@informatimago.com> writes:

>And remember, the world IS non functional: when
>you break a tea cup, you can't keep a pointer to the unbroken tea cup!

The deficiencies of the real world are hardly a good role model for
programming ;).

mkb.
From: Pascal Bourguignon
Subject: Re: `let*' and functional design
Date: 
Message-ID: <87r7e9o0rw.fsf@thalassa.informatimago.com>
Matthias Buelow <···@incubus.de> writes:

> Pascal Bourguignon <···@informatimago.com> writes:
>
>>And remember, the world IS non functional: when
>>you break a tea cup, you can't keep a pointer to the unbroken tea cup!
>
> The deficiencies of the real world are hardly a good role model for
> programming ;).

Of course.  That's my OO indoctrination that resurfaces...


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Litter box not here.
You must have moved it again.
I'll poop in the sink. 
From: Joerg Hoehle
Subject: Re: `let*' and functional design
Date: 
Message-ID: <ur7duiya1.fsf@users.sourceforge.net>
Pascal Bourguignon <···@informatimago.com> writes:
> And remember, the world IS non functional: when
> you break a tea cup, you can't keep a pointer to the unbroken tea cup!
Pascal, what a BS argument!
Take all the pieces, put in oven, heat, put in form, mould, done.

Beside that, thanks for your always concrete pieces of code which I'm
sure help people learn Common Lisp.

As you point out, LET* is not anti-functional. E.g. Haskell's "where"
is very close to it. It's just that some uses of let* rely on
sequential programming, close to imperative programming.

Other uses of it just give names to otherwise anonymous values, which
can be much more readable in some cases than
(foo
  (bar x (zot z t) y))
to be compared with
(let* ((some-meaning-full-name (zot z t))
       (other-meaning-full-name (bar x some-meaning-full-name y)))
  (foo other-meaning-full-name))

I sometimes miss a construct in CL which catches such linear
(i.e. extremely functional) style of programming. LET* does not cut
(nor catch) it, because it's scope is too large (and thus may cause
unwanted retention of intermediate garbage data, longer than
necessary, depending on the compiler).

Regards,
	Jorg Hohle
Telekom/T-Systems Technology Center
From: Kaz Kylheku
Subject: Re: `let*' and functional design
Date: 
Message-ID: <1121803481.250122.178470@g43g2000cwa.googlegroups.com>
Joerg Hoehle wrote:
> Pascal Bourguignon <···@informatimago.com> writes:
> > And remember, the world IS non functional: when
> > you break a tea cup, you can't keep a pointer to the unbroken tea cup!
> Pascal, what a BS argument!
> Take all the pieces, put in oven, heat, put in form, mould, done.

Computer data objects are no different. If you change an object, you
can't keep a pointer to the changed one.

The world is functional only when you make copies of things based on
other things.

Only, you can't do substructure sharing with ordinary objects, yet have
the illusion of a completely distinct object.

You can't have a teacup whose handle is implemented as a reference to
the handle of another teacup. :)

Lots of computer data objects work that way; namely the ones which are
not made up of pointers to things.

Of course you can use an object as a component of another one, but that
component cannot then be part of a third one still.

> Beside that, thanks for your always concrete pieces of code which I'm
> sure help people learn Common Lisp.
>
> As you point out, LET* is not anti-functional. E.g. Haskell's "where"
> is very close to it. It's just that some uses of let* rely on
> sequential programming, close to imperative programming.

Some uses of LET also rely on sequential programming.

(let ((a (funcall generator))
      (b (funcall generator))
      (c (funcall generator)))
  ...)

A B and C are elements pulled in order from the generated sequence, and
the block may depend on that order. If the generator makes
monotonically increasing integers then you can rely that A <= B <= C.
From: N. Raghavendra
Subject: Re: `let*' and functional design
Date: 
Message-ID: <86hdf1dcnc.fsf@riemann.mri.ernet.in>
At 2005-07-08T18:15:15+05:30, N. Raghavendra wrote:

> This seems to indicate that the use of `let*' is deprecated as far as
> functional programming is concerned.  Why is this so?  Is it common
> practice not to use `let*'?

Many thanks to everyone who replied to my query.  I am learning
Lisp, and will take time to understand several of the responses.
And, yes, I will continue using `let*' wherever needed.

Raghavendra.

-- 
N. Raghavendra <·····@mri.ernet.in> | See message headers for contact
Harish-Chandra Research Institute   | and OpenPGP details.