Today's reading, Section 4.6 of Tucker and Noonan's book, is titled "Scope, Visibility, and Lifetime." It does a pretty good job of explaining scope together with one meaning of visibility. (The word "visibility" means something quite different for some authors.) However, there is no mention at all of lifetime other than in the section heading. Thus, our main focus will necessarily be on lifetime, though I'll also review scope briefly and mention another meaning of "visibility".
Scope is an issue because two variables (or other entities) can
coincidentally have the same name. For example, in the following
example, it is completely coincidental that the two procedures both
use the name x
for their parameters. The second one
could be changed to use n
and the program would be
fundamentally the same.
float foo(float x){ return x * 3.14; } int bar(int x){ return x + 1; }
The scoping rules determine which variable named x
is
being referenced by any particular occurance of the name
x
in a program. Usually this is relatively
straightforward. For example, in the code shown earlier, the
x
that is multiplied by 3.14 is clearly the parameter of
foo
, not the identically named parameter of
bar
. Occasionally the determination of scope gets a bit
more interesting. Let's look at a few examples of increasing
complexity:
class Example1 { int n = 17; void foo(){ System.out.println(n); int n = 42; System.out.println(n); bar(); } void bar(){ System.out.println(n); } } class Example2 { int n = 17; void foo(){ int before = n; int n = 42; int after = n; System.out.println(before); System.out.println(after); } } class Example3 { int n = 17; void foo(){ int before = n, n = 42, after = n; System.out.println(before); System.out.println(after); } } class Example4 { int n = 17; void foo(){ int before = n, n = n+1, after = n; System.out.println(before); System.out.println(after); } }
Tucker and Noonan use the word "visibility" to discuss the fact
that a variable might be in scope except for that it is being shadowed
by a more local declaration with the same name. For example, in their
terminology, each of the three Example classes contains an instance
variable n
that is "invisible" within the remainder of
the foo
method after the local variable n
is
declared.
The same word, "visibility," is used by some other authors to
refer to a different phenomenon: a name might inaccessible due to a
modifier such as private
. An alternative term for this
is "accessibility". This issue, whatever it is called, generally
applies to "qualified names," i.e., names written using the dot
notation in languages such as Java. For example, the qualified name
someInstance.someVariable
might be invisible/inaccessible
in some portions of the program due to a private
modifier.
Moving on to "lifetime," the issue is how long a specific storage location remains associated with a particular variable. For example, consider a local variable within a procedure. Is a new storage location allocated every time the procedure is called, or is the same one used each time? One reason why this matters is because of procedures such as these:
int foo(int n){ int m; if(n != 0) m = n; return m; } void bar(){ System.out.println(foo(42)); System.out.println(foo(0)); }
Another reason why the same question matters is because of recursive procedures, such as the old favorite:
int fact(int n){ if(n == 0) return 1; else return n * fact(n-1); }
The endpoint (if any) for a variable's lifetime is also a topic of interest. Consider, for example, the following Scheme code:
(define make-adder (lambda (x) (lambda (y) (+ x y)))) (define add3 (make-adder 3)) (define add7 (make-adder 7)) (add3 10) (add7 20)
In case you think that fussing over details of lifetime (and how it might differ from scope) is just a game for pointy-headed academics, there was a great blog post just last week from Paul Vick, who is Microsoft's Technical Lead for Visual Basic .NET. He is agonizing over just this kind of issue in that language.