To demonstrate that non-lexical scoping does exist and can be useful, we present the following example from Emacs Lisp, which is a “dynamically scoped” language.
(defvar currency-abbreviation "USD") (defun currency-string (units hundredths) (concat currency-abbreviation (number-to-string units) "." (number-to-string hundredths))) (defun french-currency-string (units hundredths) (let ((currency-abbreviation "FRF")) (currency-string units hundredths)))
The question to focus on here is: what does the identifier
currency-abbreviation refer to in the
function? The answer, in Emacs Lisp, is that all variable bindings go
onto a single stack, and that
currency-abbreviation refers to the
topmost binding from that stack which has the name
“currency-abbreviation”. The binding that is created by the
defvar form, to the value
"USD", is only relevant if none
of the code that calls
currency-string rebinds the name
“currency-abbreviation” in the meanwhile.
The second function
french-currency-string works precisely by
taking advantage of this behaviour. It creates a new binding for the
name “currency-abbreviation” which overrides the one established by
;; Note! This is Emacs Lisp evaluation, not Scheme! (french-currency-string 33 44) ⇒ "FRF33.44"
Now let’s look at the corresponding, lexically scoped Scheme code:
(define currency-abbreviation "USD") (define (currency-string units hundredths) (string-append currency-abbreviation (number->string units) "." (number->string hundredths))) (define (french-currency-string units hundredths) (let ((currency-abbreviation "FRF")) (currency-string units hundredths)))
According to the rules of lexical scoping, the
currency-string refers to the
variable location in the innermost environment at that point in the code
which has a binding for
currency-abbreviation, which is the
variable location in the top level environment created by the preceding
(define currency-abbreviation …) expression.
In Scheme, therefore, the
french-currency-string procedure does
not work as intended. The variable binding that it creates for
“currency-abbreviation” is purely local to the code that forms the
body of the
let expression. Since this code doesn’t directly use
the name “currency-abbreviation” at all, the binding is pointless.
(french-currency-string 33 44) ⇒ "USD33.44"
This begs the question of how the Emacs Lisp behaviour can be
implemented in Scheme. In general, this is a design question whose
answer depends upon the problem that is being addressed. In this case,
the best answer may be that
currency-string should be
redesigned so that it can take an optional third argument. This third
argument, if supplied, is interpreted as a currency abbreviation that
overrides the default.
It is possible to change
french-currency-string so that it mostly
works without changing
currency-string, but the fix is inelegant,
and susceptible to interrupts that could leave the
currency-abbreviation variable in the wrong state:
(define (french-currency-string units hundredths) (set! currency-abbreviation "FRF") (let ((result (currency-string units hundredths))) (set! currency-abbreviation "USD") result))
The key point here is that the code does not create any local binding
for the identifier
currency-abbreviation, so all occurrences of
this identifier refer to the top level variable.