CSE341 Notes for Wednesday, 4/22/09

I began by discussing the next programming assignment. I won't repeat that here since the assignment writeup contains the essential information.

Then I started a new topic: lexical scope. This is just one example of a number of related topics that have to do with the static properties of a program versus the dynamic properties of a program. The terms compile time and run time are related terms because we can think of these as the static properties that can be deduced ahead of time by a program like a compiler versus the dynamic properties that are apparent only when the program actually executes.

Lexical scope is a static property, which is why it is sometimes referred to as static scoping (e.g., in the wikipedia entry about scope). I started a two-column list of static versus dynamic properties:

        static                    dynamic
        before execution          during execution
        compile-time              run-time
        compilers                 interpreters
        lexical scope             dynamic scope
                                  control flow (what calls what)
Lexical scope will be familiar because Java uses it. Consider, for example, the following program:

        public class Scope {
            public static int x = 10;
        
            public static void main(String[] args) {
                System.out.println(x);
                if (x > 0) {
                    int x = 20;
                    System.out.println(x);
                }
                int x = 30;
                System.out.println(x);
            }
        }
In Java, every set of curly braces introduces a new lexical scope (a new region of the program known as a block). In the program above, there is an outer scope for the overall class and an inner scope for method main and for the if:

      +------------------------------------------------+
      | public class Scope {                           |
      |     public static int x = 10;                  |
      |   +------------------------------------------+ |
      |   | public static void main(String[] args) { | |
      |   |     System.out.println(x);               | |
      |   |   +----------------------------+         | |
      |   |   | if (x > 0) {               |         | |
      |   |   |     int x = 20;            |         | |
      |   |   |     System.out.println(x); |         | |
      |   |   | }                          |         | |
      |   |   +----------------------------+         | |
      |   |     int x = 30;                          | |
      |   |     System.out.println(x);               | |
      |   | }                                        | |
      |   +------------------------------------------+ |
      | }                                              |
      +------------------------------------------------+
We want to pay attention to the identifier "x" as it is used in each of these scopes. There is a global variable x declared in the outer scope that is printed by the first call on println. There is a local x that shadows the global x that is defined inside the if block and is printed by the second println. And there is a different local x that shadows the global x and that is printed by the third println. So the output of this program is 10, 20, 30.

I briefly mentioned that there is a different approach to scope called dynamic scope in which each function call opens a new scope. For example, if you defined Java methods in the following way:

        public void f1() {
            System.out.println(x);
        }

        public void f2() {
            int x = 3;
            f1();
        }
Under dynamic scope rules, you could call f2 and the method f1 would use f2's local variable x. Dynamic scope isn't used in many languages, so I said that we wouldn't devote much time to it.

I then spent some time discussing why it is difficult in a language like Java to create a closure. We looked again at the main method from our scope example and I asked people to consider what would be involved in defining a closure at three different points of execution:

        public static void main(String[] args) {
            System.out.println(x);
            // form a closure here
            if (x > 0) {
                int x = 20;
                System.out.println(x);
                // form a closure here
            }
            int x = 30;
            System.out.println(x);
            // form a closure here
        }
In each case we would have to preserve the environment that existed at that point in program execution. It's important to realize that if we were to form a closure to define, say, a function, then the function we define might not be called until much later in time. The biggest problem here comes from the local variables. In Java we expect local variables to come into existence and then to go away when we exit a scope. To form a closure, we'd have to somehow preserve those local variables for a longer period of time.

I quickly pointed out that in Java, you can have an inner class that makes reference to fields and methods from the other class, as in:

        public class ArrayIntList extends AbstractIntList {
             {
                private int position;      // current position within the list
                private boolean removeOK;  // whether it's okay to remove now
        
                // post: constructs an iterator for the given list
                public ArrayIterator() {
                    position = 0;
                    removeOK = false;
                }
        
                public boolean hasNext() {
                    return position < size();
                }
        
                public Integer next() {
                    if (!hasNext())
                        throw new NoSuchElementException();
                    int result = get(position);
                    position++;
                    removeOK = true;
                    return result;
                }
        
                public void remove() {
                    if (!removeOK)
                        throw new IllegalStateException();
                    ArrayIntList.this.remove(position - 1);
                    position--;
                    removeOK = false;
                }
            }
        }
This a common pattern in Java to implement iterators as inner classes. One of the key lines of code to look at is the body of the hasNext method. The iterator has a field called position that it compares to the value of "size()". That's a method call, but on what method? It's certainly not a call on "this.size()" because the iterator does not have a method called size. Instead Java considers this a call on the size method of the outer class.

But how is this done? How can an object of an inner class call a method of the outer class? The answer is that Java provides something like a function closure. The inner class exists within an enviroment of an outer class with fields and methods. The inner object is constructed so that it has access to this outer environment. In other words, the inner object has a closure relative to this outer environment.

You can see this even more clearly in the remove method that has a call on the outer objects remove method. The call is on "ArrayIntlist.this.remove" which is a notation that specifically says, "call this on the outer object's remove method." I said that we would discuss this more in Friday's lecture.


Stuart Reges
Last modified: Fri Apr 24 07:19:01 PDT 2009