We also discussed how these rules would change for the hypothetical language I described on Friday as "Dynamic Java" (a version of Java that uses dynamic scope). To keep things manageable, I said that we'll assume that we are just worrying about methods and not worrying about control structures like if/else, for loops, while loops, etc. The rules of Dynamic Java would be similar. The major differences come from the definition of what constitutes a scope and allowing inner and outer scopes to have definitions of the same local variable:
public class Test { public static int x = 3; public static void one() { x *= 2; System.out.println(x); } public static void two() { int x = 5; one(); System.out.println(x); } public static void main(String[] args) { one(); two(); int x = 2; one(); System.out.println(x); } }As we saw, it produces the output 6, 12, 5, 24, 2. Below is an equivalent shell script program. It is not important that you understand details such as how it is performing the equivalent of "x *= 2". The important thing to look at is the structure of functions, function calls and definitions of local variables:
#!/bin/sh x=3 one() { x=`expr 2 \* $x` echo $x } two() { local x=5 one echo $x } main() { one two local x=2 one echo $x } mainThis shell script behaves exactly the way that we predicted the Dynamic Java program would behave, producing the output 6, 10, 10, 4, 4.
I mentioned that there will be a midterm question about lexical versus dynamic scope, although I won't have a "hole in scope" issue to deal with on the exam.
I then asked people to think about how scope works in ML. ML uses lexical scoping, but where do the scopes come from? Function declarations introduce a scope for the parameters that are listed for the function, as in:
fun f(m, n) = [this creates a local scope for identifiers m and n]; (* m and n are not visible here after the function definition *)We also get a different local scope for each let construct. You can think of each "let" as introducing its own scope box. Because there can be let constructs inside of let constructs, we can have scopes inside of scopes.
On Friday we looked at this example:
val x = 3; fun f(n) = x * n; f(8); val x = 5; f(8);The function definition mentions a parameter n, which means that it is a bound variable (the function indicates how it is to be bound--to the value passed as a parameter to the function). But the function body also refers to x without saying how x is bound. We refer to variables like x as free variables. So the big question has to do with how the value of x is determined on various calls made to the function.
Most people seemed to understand that the function was going to use the value of x that was in effect when the function was defined. So the function will always multiply its argument n by 3 even if we redefine the free variable x to be 5. I said that I want to talk about the mechanism that makes this work in ML. That will be a major topic in Wednesday's lecture.
We also discussed in more detail the example from Friday's lecture with nested scopes introduced by let constructs:
val y = 2; fun f(n) = let val x = let val n = 3 in 10 * (n + y) end val y = 100 * x in x + y + n end;Inside function f, when we go to compute the expression 10 * (n + y), we have to figure out which n and y to use. We find a local definition for n in that innermost scope, so we use that. There is a definition for y in the containing scope, but that definition comes after this one and order matters (it's another case of "hole in scope"). So in this case, the y that is found is the y from the global environment and we compute the answer as 10 * (3 + 2) or 50.
We then compute the value of y to be 100 * x. In this case, we find a local x in the current scope, so we use that and compute y to be 5000.
The final expression we have to evaluate is x + y + n. We find local definitions for x and y (50 and 5000) and n refers to the value passed as a parameter. Notice that it is not a conflict to have n redefined in an inner scope to be 3. At this level we see just the parameter n. So if the function is called with f(6), the result is 5056.