A few days ago, I talked everybody to sleep
about special variables and dynamic bindings in Lisp. I somehow managed
to avoid this topic for years, but then I finally had to understand it
to fix subtle issues in our code when dealing with what I
thought
were simple global variables.
In Lisp, you usually declare a global variable using
defvar
and
defparameter
- but this way, the variable not only becomes global,
but also
special. They are probably called
special because of
the special effects that they display - see my
blog entry for an
idea of the confusion this caused to a simple-minded C++ programmer (me).
Most of the time, I would use
defvar
to emulate the effect of a
"file-global" static variable in C++, and fortunately, this can be
implemented in a much cleaner fashion using a
let
statement
at the right spot. Example:
// C++, file foobar.C
static int globalFoo = 42;
int foobar(void)
{
return globalFoo * globalFoo;
}
int foobar2(int newVal)
{
globalFoo = newVal;
}
;; Lisp
(let ((globalFoo 42))
(defun foobar1()
(* globalFoo globalFoo))
(defun foobar2(newVal)
(setf globalFoo newVal))
)
The
let
statement establishes a binding for
globalFoo
which is only
accessible within
foobar1
and
foobar2
. This is even better than
a static global variable in C++ at file level, because this way precisely
the functions which actually have a business with
globalFoo
are
able to use it; the functions
foobar1
and
foobar2
now share a
variable. We don't have to declare a global
variable anymore and thereby achieve better encapsulation
and at the same
time avoid special variables with their amusing special effects. Life is good!
This introduces another interesting concept in Lisp: Closures,
i.e. functions with references to variables in their lexical context.
More on this hopefully soon.