Local declarations

There are two widely-used forms of local declaration in Lisp: let and let*.


The purpose of local declarations is, just as in any other language, to name intermediate results. The scope of identifiers created by a let is lexical, that is the let expression, but their extent is indefinite (see anonymous functions). let looks like this: (let ((id-1 exp-1) ... (id-m exp-m)) exp-m+1 ... exp-n) Each of the expressions exp-i, i<=m, is evaluated and the resulting values are associated with the corresponding id-i. Subsequently, the expressions exp-i, m<i<=n are evaluated in order. The result of the let expression is the result of exp-n. user> (let ((x (+ 1 2)) (y ticks-per-second)) (+ (* x x x) (* y y))) 1000027.00000000 Note that the input expression extends over more than one line. Lisp will keep waiting for input as long as the number of close parentheses is less than the number of open parentheses. In this example, x is initialized with the value 3 and y with the value 1000.00000000000. The result is the sum of x cubed and y squared.


Syntactically, let* is the same as let, as shown above, with the substitution of let* for let. The semantic difference with let* is that each expression exp-i can refer to the results of earlier initialization expressions. Whereas with let, the exp-i were evaluated first and then associated with the corresponding id-i, with let*, each exp-i is evaluated and the result associated with the corresponding id-i before starting on the evaluation of the exp-i+1. In effect, let* is equivalent to: (let ((id-1 exp-1)) (let ((id-2 exp-2)) ... (let ((id-m exp-m)) exp-m+1 ... exp-n) ... )) This means that, a reference to id-1 in exp-2 will evaluate to the result of exp-1. Here are some examples to help illustrate the difference: user> (let* ((x (+ 1 2)) (y ticks-per-second)) (+ (* x x x) (* y y))) 1000027.00000000 user> (let* ((x (+ 1 2)) (ticks-per-second 5) (y ticks-per-second)) (+ (* x x x) (* y y))) 52 user> (let ((x (+ 1 2)) (ticks-per-second 5) (y ticks-per-second)) (+ (* x x x)(* y y))) 1000027.00000000 In the first example, we have replaced let by let* and the result is the same as it was before. However, if we introduce a binding of ticks-per-second to the value 5, this affects the value of y. In the third example, we replace the let* of the second example, then the initial value of y is that of ticks-per-second in the enclosing lexical scope of the let expression and the result is that we got in the previous set of examples.
Julian Padget, jap@maths.bath.ac.uk, this version December 9, 1994