CSC447

Concepts of Programming Languages

Dynamic and Static Scope

Instructor: James Riely

Declarations and Initializers

C Initialization

  • Which x in the initializer, x + 1

int main (void) {
  int x = 10;
  {
    int x = x + 1;
    printf ("x = %08x\n", x);
  }
  return 0;
}
          

$ gcc -o scope scope.c

$ gcc -Wall -o scope scope.c
scope.c: In function ‘main’:
scope.c:5:7: warning: unused variable ‘x’ [-Wunused-variable]
scope.c:7:9: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]

$ ./scope 
x = 00000001
          

Java Initialization

  • Java requires that all variables be initialized before use.

class C {    
    public static void main (String[] args) {
        int x = 1 + x;
        System.out.printf ("x = %08x\n", x);
    }    
}

x.java:3: error: variable x might not have been initialized
        int x = 1 + x;
                    ^
1 error

Scala Initialization

  • Scala variables and fields are set to 0 before the initialization code is run
  • Recursion is allowed when initializing fields

scala> val x:Int = 1 + x 
x: Int = 1

Dynamic and Static Scope

What Does This Program Do?

  • What does this program do?
  • Using Scala syntax, but various different semantics
    • not just Scala's semantics

var x:Int = 10
def foo () = 
  x = 20

def bar () = 
  var x:Int = 30
  foo ()

bar ()
println (x)
          

Static Scope

  • Static scope: identifiers are bound to the closest binding occurrence in an enclosing block of the program code
  • Static scoping property: We can rename any identifier, so long as we rename it consistently throughout its scope
    (and so long as the new name we have chosen does not appear in the scope)
  • Also known as lexical scope

Dynamic Scope

  • Dynamic scope: identifiers are bound to the binding occurrence in the closest activation record.
  • Consistent renaming may break a working program!

Dynamic Scope

  • Where could z come from?
    
    ...
    def g (x:Int) : Int = 
      var y:Int = x * 2
      z * x * y          // x and y are local; z is non-local
                  
  • Dynamic scope:
    • non-locals are not resolved (bound) until runtime
    • to resolve non-local identifier, look at the callers

Static vs Dynamic Scope

  • Scala uses static scope (prints 20)
  • Most languages do use static scope

var x:Int = 10
def foo () : Unit =
  x = 20

def bar () : Unit = 
  var x:Int = 30
  foo ()

bar ()
println (x)
          

Static vs Dynamic Scope

Bash (prints 10):


x=10
function foo() {
    x=20
}
function bar() {
    local x=30
    foo
}
bar
echo $x
          

Static vs Dynamic Scope

C functions (prints 20):


int x = 10;
void foo () {
    x = 20;
}
void bar () {
    int x = 30;
    foo ();
}
int main () {
    bar ();
    printf ("x=%d\n", x);
}
        

Static vs Dynamic Scope

C macros (prints 10):


int x = 10;
#define foo() { \
  x = 20; \
}
#define bar() { \
    int x = 30; \
    foo (); \
}
int main () {
    bar ();
    printf ("x=%d\n", x);
}
        

Static vs Dynamic Scope

Python (prints 20):


def main():
  def foo ():
    nonlocal x
    x = 20

  def bar ():
    x = 30
    foo ()

  x = 10
  bar ()
  print (x)

main()

Static vs Dynamic Scope

Python (prints 20):


def foo ():
  global x
  x = 20

def bar ():
  x = 30
  foo ()

x = 10
def main():
  bar ()
  print (x)

main()

Python

Python global scope is not static


def useX():
  print (x)

def defX():
  global x
  x = 1
        

>>> useX()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in useX
NameError: name 'x' is not defined
>>> defX()
>>> useX()
1
        

Static vs Dynamic Scope

  • Well-known PLs have included dynamic scoping...
    • Lisp, Perl, ...
  • ...and later added static scoping!

Static vs Dynamic Scope

Emacs Lisp (prints "10"):


(let ((x 10))
  (defun foo ()
    (setq x 20))
  (defun bar ()
    (let ((x 30))
      (foo)))
  (bar)
  (message (int-to-string x)))
        

Static vs Dynamic Scope

Common Lisp (prints 20):


(let ((x 10))
  (defun foo ()
    (setq x 20))
  (defun bar ()
    (let ((x 30))
      (foo)))
  (bar)
  (print x))
        

Static vs Dynamic Scope

Scheme (prints 20):


(let ((x 10))
  (define (foo)
    (set! x 20))
  (define (bar)
    (let ((x 30))
      (foo)))
  (bar)
  (display x)
  (newline))
        

Static vs Dynamic Scope

Perl (prints 10):


local $x = 10;
sub foo {
    $x = 20;
}
sub bar {
    local $x = 30;
    foo ();
}
bar ();
print ($x);
        

Static vs Dynamic Scope

Perl (prints 20):


my $x = 10;
sub foo {
    $x = 20;
}
sub bar {
    my $x = 30;
    foo ();
}
bar ();
print ($x);