LEARNING LISP

Contents | A Style of Programming | Maps

Scope Considerations

Lisp functions live, work, and play in an environment. We learned how to save that contents of the environment a while back. The purpose of this chapter is to describe this environment and the effects of lambda-binding on it.

When you enter the Lisp system, there are some predefined Lisp functions [the built-ins] and two predefined variables, T and NIL. Using SETQ to define your own variables causes Lisp to set up an area of storage for holding the value of that variable. Thus, evaluating the expression, (SETQ A 10), Lisp binds the value of 10 to the variable A, setting up something like this:

A Cell

Since we are not currently evaluating any function, this value of A is said to be in the global environment. The global environment is the state of affairs at the highest level [that is, not inside any functions]. Any subsequent use of the SETQ function with A will change the value in A.

The interaction of the assignment with the evaluation of user-defined functions is interesting. Remember that the global value of variables are left untouched even though the local variables in the function may have the same name. Let's define some functions with which we can experiment.

:(SETQ X 10)

  10

:(SETQ Y 20)

  20

:(DEFINE (FUN1 (LAMBDA (X)

:    (PRINT X)

:    (PRINT Y)

:    (FUN2 X Y)

:    (FUN3 (ADD X 1)))))

  FUN1

:(DEFINE (FUN2 (LAMBDA (X Z)

:    (PRINT X)

:    (PRINT Y)

:    (PRINT Z)

:    (SETQ Y 5))))

  FUN2

:(DEFINE (FUN3 (LAMBDA (R)

:    (PRINT R)

:    (PRINT X)

:    (PRINT Z))))

  FUN3

:(FUN1 2)
  2
  20
  2
  20
  3
  2
  5

:X

  10

:Y

  5
We defined three somewhat contrived functions. We begin the process of evaluation by typing "(fun1 2)". When FUN1 is evaluated, the value of its actual argument 2 is bound to the local variable X. But X already exists in the global environment by means of the SETQ function. Lisp always checks to see if a conflict between a global variable and a local variable esists. If there is such a conflict, the new value is stacked on top of the older value or values. This can be better understood with the help of the following picture:

Variable value stacking

The value of the atom in evaluation is designated by the value at the top of its stack. Therefore, the value of X at this point is 2 and the value of Y is 20. These values are called a local environment.

After the function, in which the local variable is defined, ends, the topmost [current] value is removed from the stack. Thus, the older value is returned. Note that the global environment can't be removed from the stack in this way.

While inside the function, the local value acts like a global value to all the functions called by the first function. The same is true for all deeper levels of function calls.

Back to our example. We are now inside the function FUN1. The first expression to be evaluated is "(PRINT X)". This causes the current value of X to be printed. Since X is a local variable to this function, the current value of X is the value of the argument supplied in the call of the function FUN1. This is the number 2 which we typed ourselves. Therefore, it is the first 2 in the output. The next expression, "(PRINT Y)", prints the global value of Y, which is 20.

The next expression, "(FUN2 X Y)" is a call to the function FUN2. The formal arguments for FUN2 are X and Z. Because X already exists in the environment [twice, in fact], we must again "stack" the new value, 2, on top of the older value, 2. [Note that the old value and the new value are the same. Lisp does not care, it will save the old one anyway.] Also, the value of 20 is bound to Z. The new environment looks like this:

The environment after calling FUN2

We are now inside of FUN2 [which is inside of FUN1]. The value of X is printed as 2, in the third output line. Then, the value of Y is printed. This is 20. Next, the value of Z in printed [20, the fifth line]. We then use the SETQ function to assign a value to the variable Y. Therefore, Y now has the value of 5. If Y had been a local variable in FUN2, then the assignment would have been broken when leaving FUN2. However, since Y is global, this assignment of value is permanent, at least until the next assignment. The action of changing the value of a variable which is not one of the function's formal arguments is called a side effect. Side effects are usually very nasty, and should be avoided.

FUN2 now exits, returning the value of Y [which is thrown away]. We are now back in FUN1 and the uppermost level has been removed from the stack.

The environment after FUN2 is finished

Next, we call FUN3 with the expression "(FUN3 (ADD X 1))". The value of X inside FUN1 is 2. Therefore, (ADD X 1) evaluates to 3. The value of the formal argument in FUN3, namely R, has the value of 3 bound to it.

The environment after calling FUN3

The value of R is now printed on the sixth line. The variables X and Y are also printed. The question is, which X and which Y? Neither are local to FUN3. For X, we use the value which was available in the previous environment, that is FUN1. There, X had the value 2. Therefore, a 2 is printed for X. What about Y? Y isn't local to any function, so Lisp uses the global value of 5. [Note that the global value was assigned from inside the function FUN2.]

The last number printed is the value returned from FUN1. FUN1 is returned from FUN3, which is returned from PRINT. Remember that everything in Lisp returns a value, even PRINT.

Contents | A Style of Programming | Maps