Prev Up Next

Scheme's variables have lexical scope, ie, they are visible only to forms within a certain contiguous stretch of program text. The global variables we have seen thus far are no exception: Their scope is all program text, which is certainly contiguous.

We have also seen some examples of local variables. These were the lambda parameters, which get bound each time the procedure is called, and whose scope is that procedure's body. Eg,

(define x 9)
(define add2 (lambda (x) (+ x 2)))

x        => 9

(add2 3) => 5
(add2 x) => 11

x        => 9

Here, there is a global x, and there is also a local x, the latter introduced by procedure add2. The global x is always 9. The local x gets bound to 3 in the first call to add2 and to the value of the global x, ie, 9, in the second call to add2. When the procedure calls return, the global x continues to be 9.

The form set! modifies the lexical binding of a variable.

(set! x 20)

modifies the global binding of x from 9 to 20, because that is the binding of x that is visible to set!. If the set! was inside add2's body, it would have modified the local x:

(define add2
  (lambda (x)
    (set! x (+ x 2))
    x))

The set! here adds 2 to the local variable x, and returns that value. (In terms of effect, this procedure is indistinguishable from the previous add2.) We can call add2 on the global x, as before:

(add2 x) => 22

(Remember global x is now 20, not 9!)

The set! inside add2 affects only the local variable used by add2. Although the local variable x got its binding from the global x, the latter is unaffected by the set! to the local x.

x => 20

Note that we had all this discussion because we used the same identifier for a local variable and a global variable. In any text, an identifier named x refers to the lexically closest variable named x. This will shadow any outer or global x's. Eg, in add2, the parameter x shadows the global x.

A procedure's body can access and modify variables in its surrounding scope provided the procedure's parameters don't shadow them. This can give some interesting programs. Eg,

(define counter 0)

(define bump-counter
  (lambda ()
    (set! counter (+ counter 1))
    counter))

The procedure bump-counter is a zero-argument procedure (also called a thunk). It introduces no local variables, and thus cannot shadow anything. Each time it is called, it modifies the global variable counter -- it increments it by 1 -- and returns its current value. Here are some successive calls to bump-counter:

(bump-counter) => 1
(bump-counter) => 2
(bump-counter) => 3

Prev Up Next

Log in or register to write something here or to contact authors.