From: cogen
Subject: LET vs. LET*
Date: 
Message-ID: <1782@xn.LL.MIT.EDU>
Too many times I find myself changing a LET to a LET* as I add more binding
forms to it. To avoid this modification (and the necessity of reformatting
caused by the extra character '*') I have often considered using LET* by
default, reserving LET for those rare occasions where I really require parallel
binding. But I wonder, is there a performance reason not to use LET*? Is it the
case that parallel binding is generally more efficient? Probably the answer
depends on the Lisp, but there may be a general answer too. The Lisps I
commonly use are KCL and Symbolics Lisp.

-- David Cogen.

From: Eliot Handelman
Subject: Re: LET vs. LET*
Date: 
Message-ID: <15151@phoenix.Princeton.EDU>
In article <····@xn.LL.MIT.EDU> ·····@XN.LL.MIT.EDU (cogen) writes:
;Too many times I find myself changing a LET to a LET* as I add more binding
;forms to it. To avoid this modification (and the necessity of reformatting
;caused by the extra character '*') I have often considered using LET* by
;default, reserving LET for those rare occasions where I really require parallel
;binding. But I wonder, is there a performance reason not to use LET*? Is it the
;case that parallel binding is generally more efficient? Probably the answer
;depends on the Lisp, but there may be a general answer too. The Lisps I
;commonly use are KCL and Symbolics Lisp.
;
;-- David Cogen.

I can't imagine any big performance issues, but some lisps -- not KCl --
turn LET(*) into lambdas, ie into function calls (I'm prepared
to stand corrected). So a LET turns into one call, like this:
(LET (x y) body) => ((LAMBDA (X Y) body) nil nil)
and  LET* turns into as many calls as there are let forms, like this:

(LET* ((x 1) (y x)) body) =>

((lambda (x)
  ((lambda (y)
     body)
   x))
 1)

-- this is how Lucid handles LET's, anyhow. I assume that LET* is in those
cases negligibly slower.
From: Barry Margolin
Subject: Re: LET vs. LET*
Date: 
Message-ID: <35361@think.Think.COM>
In most modern Lisp systems LET and LET* should be approximately equivalent
in performance.  The reason that LET* has the "starred" name is mostly
historical.  In the MacLisp days neither LET nor LET* were primitives of
the language.  LET was simply a macro that expanded into a call to an
anonymous lambda expression:

	(let ((var1 expr1)
	      (var2 expr2))
	  <body>)

expanded into

	((lambda (var1 var2) <body>) expr1 expr2)

The parallel binding was a natural result of this expansion.  In these
systems LET was also often more efficient than LET* because LET* would
usually expand into nested LETs, which would then expand into nested
procedure calls, so the expense of LET* would be linear in the number of
variables, while LET would have constant overhead.  Note that this expense
wasn't actually necessary -- a good compiler could merge these nested
procedure calls (I think this is called beta-reduction), but I don't know
how many of the compilers developed in the 70's did this.

--
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: Jeff Dalton
Subject: Re: LET vs. LET*
Date: 
Message-ID: <2178@skye.ed.ac.uk>
In article <·····@think.Think.COM> ······@nugodot.think.com.UUCP (Barry Margolin) writes:
 >The parallel binding was a natural result of this expansion.  In these
 >systems LET was also often more efficient than LET* because LET* would
 >usually expand into nested LETs, which would then expand into nested
 >procedure calls, so the expense of LET* would be linear in the number of
 >variables, while LET would have constant overhead.  Note that this expense
 >wasn't actually necessary -- a good compiler could merge these nested
 >procedure calls (I think this is called beta-reduction), but I don't know
 >how many of the compilers developed in the 70's did this.

But compilers may well have treated calls where the function position
was a lambda-expression specially: that is, they could have treated it
as a declaration of local variables rather than as a function call.
And then the nested locals of a LET* might be compined into one stack
allocation by a variety of optimization techniques.  I would think
beta-reduction might be what happens in compilers that take a lambda
calculus approach (such as Steele's Rabbit compiler for Scheme and
perhaps the Lucid Common Lisp compiler), but more ordinary compilers
might still eliminate the overhead in one way or another.
From: Eric Benson
Subject: Re: LET vs. LET*
Date: 
Message-ID: <2211@heavens-gate.lucid.com>
In article <····@xn.LL.MIT.EDU>, ·····@XN.LL.MIT.EDU (cogen) writes:
> Too many times I find myself changing a LET to a LET* as I add more binding
> forms to it. To avoid this modification (and the necessity of reformatting
> caused by the extra character '*') I have often considered using LET* by
> default, reserving LET for those rare occasions where I really require parallel
> binding. But I wonder, is there a performance reason not to use LET*? Is it the
> case that parallel binding is generally more efficient? Probably the answer
> depends on the Lisp, but there may be a general answer too. The Lisps I
> commonly use are KCL and Symbolics Lisp.

As others have indicated, there is no performance reason to prefer LET
over LET*.  Any difference in efficiency is both idiosyncratic and
insignificant, no matter what Lisp you are using.  LET* is just as
likely to be more efficient than LET as the other way around.  A much
more significant concern is programming style; what does your choice
of binding form convey to someone reading your code?  Generally
speaking, when someone uses LET they are saying "These bindings don't
depend on each other."  On the other hand, when LET* is used it says,
"The order of these bindings is important; a later initialization
depends on some earlier binding."  If you always use LET* you may lead
someone astray who is trying to understand your code.

Since it is relevant, I'm going to take this opportunity to
shamelessly plug a new book, "Lisp Style & Design," by Molly M.
Miller and Eric Benson, published by Digital Press.  It has just been
published and should appear in bookstores this month.  To quote from
the blurb on the back cover, "Lisp Style & Design explores the process
of style in the development of Lisp programs.  Style comprises
efficient algorithms, good organization, appropriate abstractions,
well-constructed function definitions, useful commentary, and
effective debugging.  Good design and style enhance programming
efficiency because they make programs easier to understand, to debug,
and to maintain."

··@lucid.com 	           	 	Eric Benson
415/329-8400 x5523                      Lucid, Inc.
Telex 3791739 LUCID                     707 Laurel Street
Fax 415/329-8480                        Menlo Park, CA 94025