The let and let* forms provide lexical scoping, which is what you
expect if you're used to programming in C or Pascal. Dynamic scoping is
what you get in BASIC: if you assign a value to a dynamically scoped
variable, every mention of that variable returns that value until you
assign another value to the same variable.
In LISP, dynamically scoped variables are called special variables. You can declare a special variable with the defvar special form. Here are some examples of lexically and dynamically scoped variables.
In this example, the function check-regular references a regular (ie,
lexically scoped) variable. Since check-regular is lexically outside of
the let which binds regular, check-regular returns the
variable's global value.
> (set 'regular 5) ;setq would make it special in CMUCL!
5
> (defun check-regular () regular)
CHECK-REGULAR
> (check-regular)
5
> (let ((regular 6)) (check-regular))
5
In this example, the function check-special references a special (ie,
dynamically scoped) variable. Since the call to check-special is
temporally inside of the let which binds special,
check-special returns
the variable's local value.
> (defvar *special* 5)
*SPECIAL*
> (defun check-special () *special*)
CHECK-SPECIAL
> (check-special)
5
> (let ((*special* 6)) (check-special))
6
By convention, the name of a special variable begins and ends with a *. Special variables are chiefly used as global variables, since programmers usually expect lexical scoping for local variables and dynamic scoping for global variables.
For more information on the difference between lexical and dynamic scoping, see Common LISP: the Language.