We discussed the differences between static (or lexical) scope and dynamic scope. Under static scoping, a variable name refers to the declaration of that variable with closest enclosing scope. Conversely, dynamic scope finds values for variables by finding the most recent (in terms of execution order) declaration of that variable. The differences between static and dynamic scope are most easily seen by drawing out the activation records for a program. But, just by knowing basic blocks and the control flow of a program, you can figure out the static and dynamic scoping behavior. We used the following ML code fragment to see the differences between static and dynamic scoping:Under static scoping rules, the value of a*p(2) is 25.... val a = 3; fun p(x) = x + a; let val a = 5 in a*p(2) end; ...
Under dynamic scoping rules, the value is 35.
Using the simple code example above, we drew the runtime stack of activation records to see exactly how static and dynamic scope were working. If you can draw the static and control links for the sequence of activation records properly, it is easy to see how static and dynamic scoping determine the values for variables. Static scope determines the values of variables by following access links. Dynamic scope follows control links to find values.
Once again, we walked through drawing the runtime stack for a simple example, which is very similar to our first example:... val a = 3; fun p(x) = x + a; fun q(f) = let val a = 7 in a*f(2) end; q(p); ...The runtime stack looks like the following diagram. It is simplified to only show the local variables with their values and the static links for each block.
We briefly talked about returning functions from nested scope. The main points to remember are:
- Closures are used to set the access link
- The stack discipline fails