Skip to main content

let, let

let, let*∗ Special Operator*

Syntax:

let ({var | (var [init-form])}*) {declaration}* {form}* → {result}*

let* ({var | (var [init-form])}*) {declaration}* {form}* → {result}*

Arguments and Values:

var—a symbol.

init-form—a form.

declaration—a declare expression; not evaluated.

form—a form.

results—the values returned by the forms.

Description:

let and let* create new variable bindings and execute a series of forms that use these bindings. let performs the bindings in parallel and let* does them sequentially.

The form

(let ((var1 init-form-1 )

(var2 init-form-2 )

...

(varm init-form-m))

declaration1

declaration2

...

declarationp

form1

form2

...

formn)

let, let*∗*

first evaluates the expressions init-form-1, init-form-2, and so on, in that order, saving the resulting values. Then all of the variables varj are bound to the corresponding values; each binding is lexical unless there is a special declaration to the contrary. The expressions formk are then evaluated in order; the values of all but the last are discarded (that is, the body of a let is an implicit progn).

let* is similar to let, but the bindings of variables are performed sequentially rather than in parallel. The expression for the init-form of a var can refer to vars previously bound in the let*.

The form

(let* ((var1 init-form-1 )

(var2 init-form-2 )

...

(varm init-form-m))

declaration1

declaration2

...

declarationp

form1

form2

...

formn)

first evaluates the expression init-form-1, then binds the variable var1 to that value; then it evaluates init-form-2 and binds var2, and so on. The expressions formj are then evaluated in order; the values of all but the last are discarded (that is, the body of let* is an implicit progn).

For both let and let*, if there is not an init-form associated with a var, var is initialized to nil.

The special form let has the property that the scope of the name binding does not include any initial value form. For let*, a variable’s scope also includes the remaining initial value forms for subsequent variable bindings.

Examples:

(setq a ’top) → TOP 
(defun dummy-function () a) → DUMMY-FUNCTION
(let ((a ’inside) (b a))
(format nil "~S ~S ~S" a b (dummy-function)))"INSIDE TOP TOP"
(let\* ((a ’inside) (b a))
(format nil "~S ~S ~S" a b (dummy-function)))"INSIDE INSIDE TOP"
(let ((a ’inside) (b a))
(declare (special a))
(format nil "~S ~S ~S" a b (dummy-function)))"INSIDE TOP INSIDE"
The code
(let (x)
Data and Control

(declare (integer x))
(setq x (gcd y z))
...)
is incorrect; although x is indeed set before it is used, and is set to a value of the declared type *integer* , nevertheless x initially takes on the value **nil** in violation of the type declaration.

See Also:

progv

Expanded Reference: let, let*

Basic variable binding with let

let creates local variable bindings. Each variable is bound to the result of evaluating its init-form, then the body forms are executed.

(let ((x 10)
(y 20))
(+ x y))
=> 30

Variables without init-forms default to NIL

If no init-form is provided, the variable is initialized to nil.

(let (x y z)
(list x y z))
=> (NIL NIL NIL)

Parallel binding with let

In let, all init-forms are evaluated before any variable is bound. Here, b gets the outer value of a, not the inner one.

(let ((a 1))
(let ((a 2)
(b a))
(list a b)))
=> (2 1)

Sequential binding with let*

In let*, each binding is completed before the next init-form is evaluated. Here, b sees the new value of a.

(let* ((a 1)
(b (+ a 10))
(c (+ a b)))
(list a b c))
=> (1 11 12)

Nested let for building up local context

let forms can be nested and the body is an implicit progn, returning the value of the last form.

(let ((name "Alice")
(greeting "Hello"))
(let ((message (concatenate 'string greeting ", " name "!")))
(length message)
message))
=> "Hello, Alice!"

Shadowing outer bindings

Inner let bindings shadow outer ones without affecting them.

(let ((x 100))
(list
(let ((x 1)) x)
x))
=> (1 100)