Menu

SI413 Class 13: Name, Scope, Lifetime


Reading
Sections 3.1, 3.2 (need only skim), and 3.3.1 and 3.3.6 of Programming Language Pragmatics.

Homework
  1. Consider the following fragment of code, which is part of a much larger program that uses print all the time.
    int width = 10;
    char justification = 'L';
    
    void print(string s)
    {
      int left, diff =  width - s.length();
      if (diff <= 0) { print(s); return; }
      switch(justification) {
      case 'L': left = diff;   break;
      case 'R': left = 0;      break;
      case 'C': left = diff/2; break;
      }
      for(int i = 0; i < left; ++i)         print(' ');
      print(s);
      for(int i = 0; i < width - left; ++i) print(' ');
    }
    
    void foo(double z)
    {
      justification = 'R';
      print("answer:");
      print(toString(z));
    }
    
    void bar(double z)
    {
      char justification = 'R';
      print("answer:");
      print(toString(z));
    }
    
    1. Describe the difference between calling foo versus bar assuming this language is lexically (aka statically) scoped. I.e. foo prints .... whereas bar prints ...
    2. Describe the difference between calling foo versus bar assuming this language is dymnamically scoped. I.e. foo prints .... whereas bar prints ...
    3. What does calling bar in a dynamically scoped language accomplish that is difficult to do nicely in a lexically scoped language without some additional language constructs?

Bindings
Referencing environment. To evaluate something like
3*x - 5/y + f(2.0)
requires knowing the current bindings for x, y and f. The set of current bindings is called the "referencing environment".

Object Lifetime and Storage Management
Discuss static, stack and heap allocated objects in programs. Give example of what goes on with the stack for
int g(int x) { return x*x; }
int f(int y) { int x = 3 + g(y);  return x; }
int main() { int n = 5; f(n); return 0; }
Discuss Fortan 77's lack of recursion and how this allows for static allocation of activation records.

Single Scope
Early BASIC had a single scope. So a name refered to the same object no matter where you were in a program. Consider the following program (which doesn't look like BASIC!) where we'll assume a single scope:
x = 10;

int foo(y) { x = x + y; return y; }

int main()
{
  for(i = 0; i < 100; ++i)
  {
    x = rand() % 11;
    print(x);
    print(foo(x));
  }
  print(x);
}
If you think of this program as a sort of bank simulation, where the balance x=10 is set at the start, and foo(y) represents depositing y dollars into the bank, you'll see that we have a problem. Inside the for loop we needed a temporary variable. We choose x and ... we chose poorly, because x refers to the same object everywhere in the program, and we're overwriting the global that tracks the account balance. With a single scope, this kind of error is easy to make. More importantly, you can't have recursion with a single scope!

Simple Global/local scope: lifetime decision, lexical/static vs. dynamic scope design decision
Fortan and C both have local and global scopes. However, that doesn't tell the whole story: we need to address some more semantic issues. One issue concerns lifetimes of local variables. Suupose I have
int f(int x) { int y = 0; y = y + x; return y; }
If I call f(4) I should get 4, but what if I subsequently call f(5)? Whether I get 5 or 9 depends on a language design decision about the lifetime of local variables. Do they die when they go out of scope, or do they live on as they go in and out of scope. In C they die when they go out of scope at the end of their containing block, by default. However, by prefixing the declaration with the keyword static, you get the opposite semantics, and the variable lives on until the end of the program. Fortran 77 had the same policy as C's default policy, but we talked about a funny side effect of some implementations' use of statically allocated activation records.

Yet another issue has to do with determining which bindings are active when we have non-local variable references. For example, consider the following piece of very familiar code:

int x = 10;

int foo(int y) { x = x + y; return y; }

int main()
{
  for(int i = 0; i < 100; ++i)
  {
    int x = rand() % 11;
    print(x);
    print(foo(x));
  }
  print(x);
}

We'll assume that local variables die withthe end of their enclosing block, just like C and Fortran, but we allow any { }-delimited block to define a scope. The question is this: When I call foo inside of main, does the "x" inside of foo refer to the global "x" or the "x" that's local to the for loop? In other words, do non-local references (like the "x" inside the foo function) refer to the x that is in scope at the point of the defintion of the function, or the x that is in scope at the point of the call of the function. The first approach is called lexical scope (also static scope), and the second is called dynamic scope.


Christopher W Brown
Last modified: Tue Oct 20 09:11:05 EDT 2009