LET
itself is not a real primitive in a Functional Programming Language, since it can replaced with LAMBDA
. Like this:
(let ((a1 b1) (a2 b2) ... (an bn))
(some-code a1 a2 ... an))
is similar to
((lambda (a1 a2 ... an)
(some-code a1 a2 ... an))
b1 b2 ... bn)
But
(let* ((a1 b1) (a2 b2) ... (an bn))
(some-code a1 a2 ... an))
is similar to
((lambda (a1)
((lambda (a2)
...
((lambda (an)
(some-code a1 a2 ... an))
bn))
b2))
b1)
You can imagine which is the simpler thing. LET
and not LET*
.
LET
makes code understanding easier. One sees a bunch of bindings and one can read each binding individually without the need to understand the top-down/left-right flow of 'effects' (rebindings). Using LET*
signals to the programmer (the one that reads code) that the bindings are not independent, but there is some kind of top-down flow - which complicates things.
Common Lisp has the rule that the values for the bindings in LET
are computed left to right. Just how the values for a function call are evaluated - left to right. So, LET
is the conceptually simpler statement and it should be used by default.
Types in LOOP
? Are used quite often. There are some primitive forms of type declaration that are easy to remember. Example:
(LOOP FOR i FIXNUM BELOW (TRUNCATE n 2) do (something i))
Above declares the variable i
to be a fixnum
.
Richard P. Gabriel published his book on Lisp benchmarks in 1985 and at that time these benchmarks were also used with non-CL Lisps. Common Lisp itself was brand new in 1985 - the CLtL1 book which described the language had just been published in 1984. No wonder the implementations were not very optimized at that time. The optimizations implemented were basically the same (or less) that the implementations before had (like MacLisp).
But for LET
vs. LET*
the main difference is that code using LET
is easier to understand for humans, since the binding clauses are independent of each other - especially since it is bad style to take advantage of the left to right evaluation (not setting variables as a side effect).
Originally, in Lisp, there were no lexical variables -- only dynamic ones. And
there was no SETQ or SETF, just the SET function.
What is now written as:
(setf (symbol-value '*foo*) 42)
was written as:
(set (quote *foo*) 42)
which was eventually abbreviavated to SETQ (SET Quoted):
(setq *foo* 42)
Then lexical variables happened, and SETQ came to be used for assignment to them too -- so it was no longer a simple wrapper around SET.
Later, someone invented SETF (SET Field) as a generic way of assigning values to data structures, to mirror the l-values of other languages:
x.car := 42;
would be written as
(setf (car x) 42)
For symmetry and generality, SETF also provided the functionality of SETQ. At this point it would have been correct to say that SETQ was a Low-level primitive, and SETF a high-level operation.
Then symbol macros happened. So that symbol macros could work transparently, it was realized that SETQ would have to act like SETF if the "variable" being assigned to was really a symbol macro:
(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))
foo => 42
(setq foo 13)
foo => 13
*hidden* => (13 . 42)
So we arrive in the present day: SET and SETQ are atrophied remains of older dialects, and will probably be booted from eventual successors of Common Lisp.
Best Solution
In Common Lisp, this code:
will return 15. With your proposal, I think it would see that the true-clause is a list, and automatically convert it to:
which would return 10. Is that right?
I'm not sure it's "trivial" to distinguish between "list" and "function invocation" in CL. Do you intend for this change to be non-backwards-compatible? (New and interesting Lisp dialects are always cool, but then it's not Common Lisp.) Or can you give an example of what you have in mind?