(DEFINE (SCALE S V)
(MAPCAR (LAMBDA (X) (* X S))
V))
Everything would be just peachy keen, except for one small glitch. Suppose that the programmer who wrote SCALE for some reason chose the name L rather than S to represent the scalar:
(DEFINE (SCALE L V)
(MAPCAR (LAMBDA (X) (* X L))
V))
Although the version with S works, the version with L does not work. This happens because MAPCAR also uses the name L for one of its arguments (that is, a "local" variable). The reference to L in the LAMBDA-expression in SCALE refers to the L bound in MAPCAR and not to the one bound by SCALE. In general, free variable references in one procedure refer to the bindings of variables in other procedures higher up in the chain of calls. This discipline is called dynamic scoping of variables, because the connection between binding and reference is established dynamically, changing as different procedures are executed.
That the behavior of the SCALE program depends on the choice of names for its local variables is a violation of referential transparency. The modularity of the MAPCAR abstraction has been destroyed, because no one can use that abstraction without understanding the details of its implementation. This is the famous "FUNARG problem" [Moses] [LISP History].
If we are to avoid such conflicts between different uses of the same name, we must arrange our language so that the choice of names locally cannot have global repercussions. More specifically, we must have the ability to bind a variable in such a way that it will have a truly local meaning (though in general we might not want all variables to be strictly local — we will consider later the possibility of having several types of variables).