CSE 341 -- Scope and Naming

What does a name refer to? There are many situations where the same name is used for different things.

Each identifier is in a particular namespace . The same identifier has different meanings in different name spaces - for example, in Java, the class foo is distinct from the variable foo is distinct from the label foo is distinct from the method foo. Here is a cryptic example from the Java text:

class Foo {
  Foo Foo (Foo Foo) {
    Foo:
      for (;;) {
         if (Foo.Foo(Foo) == Foo)
            break Foo;
      }
      return Foo;
  }
}

Is this good style? Well, not really. Languages have different name spaces - for example, the name spaces for functions and variables are different in CommonLisp (and most other lisp dialects). This is often criticized as a flaw in the language, since it means that a different mechanism is needed to call the function f than evaluate the variable f. The language Scheme has a single name space for both functions and variables.

Within a name space, the same name can be used many times. Examples of this include variable declarations, structure definitions, and class extensions. For example the following C program has three distinct variables x. (C allows variables to be redeclared in nested blocks - Java does not.)


int x;

main(){
  double x;

  {
    char* x;

  }

}

In most languages, an identifier is associated with inner most declaration. This is refered to as lexical scoping. The use of the same name of a variable which hides other variables is generally bad style - although many languages allow it because the alternative, insisting on distinct names would require the programmer to know all the variables in use, violating the principle of localizing information.

Some languages provide ways to access hidden variables - for example PL1 has an EXTERNAL command for accessing hidden variables. In Java hidden fields can be accessed using this.

class Planet {
   String name;

   void setName(String name){
     this.name = name;
   }
}

Object oriented languages provide facilities for accessing methods which have been overridden. For example, super is used in Java for this.

Dynamic vs. Lexical Scope

The alternative to lexical scope is dynamical scope, where the association of names is done at run time, not at compile time. Consider the following two programs:
int x = 1;

void printx() {
  printf("%d\n", x);
}

main()
{
  printx();
  {
    int x = 2;
    printf("%d\n", x);
    printx();
    {
      int x = 3;
      printf("%d\n", x);
      printx();
    }
    printf("%d\n", x);
    printx();
  }
  printx();
}


==============================

;; defvar declares and initializes a global variable

(defvar x 1)

(defun printx ()
   (print x))

(defun main ()
   (printx)
   (let ((x 2))
        (print x)
        (printx)
        (let ((x 3))
             (print x)
             (printx))
        (print x)
        (printx))
    (printx)
    nil)

The first program prints out
1
2
1
3
1
2
1
1
and the second program prints out
1
2
2
3
3
2
2
1
The difference is that the C function printx() uses the global variable x, while the LISP function binds x at runtime to an instance of the variable x.

Most languages have abandoned the use of dynamical scope in favor of lexical scope. The above commonlisp example uses a global variable (via the defvar declaration). Global variables are dynamically scoped in commonlisp, but other variables are lexically scoped. Older dialects of lisp used dynamic scope. Scheme is strictly lexically scoped.

Macro expansion can be viewed as using dynamic scope - for example, Swap will use the version of temp corresponding to where Swap is called.


#define Swap(A,B)  {temp = (A); (A) = (B); (B)=temp;}