|
the correct answer as far as LET vs LET* will be lost to all the noise, but here it goes. people are just defending a historic artifact like it actually makes any kind of sense, or like it was a deliberate design choice. it's not and it wasn't, and you're totally correct as far as it being confusing. common lisp is compromise between a variety of lisp vendors, who all were working on variously mutated dialects of the same language in the 80s, while the languages was evolving from the 60s! much of the languages functionality was added while people were figuring out how to do high level programming, so constructs that seem fundamental to us weren't introduced until much later in the process, because nobody knew that such a thing could exist. if you read documents from that era, the code that they write is wildly alien, because present day programming wasn't invented yet. things that you take for granted just didn't exist. common lisp and to a lesser extent scheme carry all that baggage for backwards compatibility. so when lisp came out, it didn't have LET, and LET didn't appear until mid 70s. and it was added as a convenience macro for ((lambda (VAR1 VAR2 ...) ...) FORM1 FORM2 ...), which is how people did dynamic binding (also called lambda-binding) for a decade prior. evolution of lisp claims that LET came from lisp machine lisp, but people were independently inventing it all over the place, as a custom macro. depending on how your lambdas were evaluated or how your LET macro was written, you're not just assigning VARs simultaneously, you might not even have a guarantee of the order of evaluation of FORMs. but it got its job done, which is according to revised scheme manual "allowing the forms for the quantities to appear textually adjacent to their corresponding variables". this was a novel convenience at some point! LET* was invented after LET as a convenience for LET, because sequential binding was even more convenient than binding in general. it would've made sense to then make LET* the "default" of some sort, but the subtle distinction stuck for reasons of legacy code, writing conventions, acquired preferences, and then it got crystalized and preserved for posterity in the common lisp standard. |
However, a let based on lambda would have parallel binding. The evaluation of the forms would mainly come from the argument evaluation order of the lambda, where the macro would have to go out of its way to screw it up.
It's worth noting that Common Lisp has optional parameters. These use sequential binding like let*. So we could translate
into The lambda is called with no arguments, so that the defaulting takes place, and that has all the semantics we need. (We could also similarly exploit &aux).The fact that CL's optional parameters use sequential binding kind of shows that it's the preferred mode.
The reason that the fixed parameters of lambda have parallel binding is that the values don't come from the lambda form itself, but from the arguments, which are already evaluated. So there is no way for a parameter value to be calculated from another parameter value. They come into existence at the same time.
Not so with optionals; they have default expressions, and those can refer to the prior variables.
Thus let came from lambda, and was understood in terms of fixed, required parameters. Required parameters come into the scope simultaneously, and so let variables came into scope simultaneously.