Un contexte est un bloc avec des variables locales. Il y a deux types de contextes : let et let*. Les deux ont la même syntaxe (à la différence du nom du contexte), mais la sémantique diffère.
Le premier paramètre est une liste de liaisons (en Anglais : bindings) entre variable-valeur. Cette liste est suivie d'un nombre arbitraire d'expressions évaluées comme un progn implicite. Chaque liaison est une liste à deux éléments : la variable et sa valeur. Exemple :
(let ((x 234)
(y 345))
(setf *p* (+ x y))
(* x y))
Ici, deux variables locales, x et y sont créées. La portée de ces deux variables est le corps du let, à savoir les deux expressions (setf *p* (+ x y)) et (* x y). La valeur de la variable x sera 234 et la valeur de la variable y sera 345. Les expressions d'initialisation des variables peuvent être arbitrairement compliquées :
* (let ((x (let ((z *read-base*)) (setf *read-base* 10) (+ z 2)))
(y (prog1 (car '(1 2 3)) (print "hello"))))
(+ x y))
"hello"
13
*
Les expressions d'initialisation sont évaluées dans l'ordre syntaxique, mais pour le let, les variables locales sont créées après toutes les initialisations :
* (let ((x 2))
(let ((x 3)
(y (+ x 2)))
(* y x)))
12
*
Dans cet exemple, deux variables portent le nom x. La portée de la première (celle initialisée à 2) couvre toutes les initialisations du let imbriqué. La valeur de l'expression (+ x 2) est donc 4. Par contre, dans le corps du let imbriqué, c'est la variable x initialisée à 3 qui couvre, ce qui explique le résultat.
Il est également possible de remplacer une initialisation de type liste à deux éléments (variable et valeur initiale) par une variable seule. Dans ce cas, la variable est initialisée automatiquement à nil. Le bon style Common Lisp exige que cette valeur implicite ne soit pas utilisée, et que la variable soit considérée comme non initialisée. Exemple :
* (let (x
(y 5))
(setf x (+ y *read-base*))
(* x y))
75
*
La seule différence entre le let et le let* est qu'avec let*, les liaisons des variables sont créées séquentiellement après chaque initialisation. Voici par conséquent le résultat de l'exemple ci-dessus avec un let* à la place du let :
* (let ((x 2))
(let* ((x 3)
(y (+ x 2)))
(* y x)))
Warning: Variable X defined but never used.
15
*
Le message de CMUCL indique que la variable x initialisée à 2 n'est jamais utilisée, car toutes les autres occurrences de x font référence à l'autre variable x.