Lexical and Dynamic Scoping

236319 Spring 2024, Prof. Lorenz


Lexical and Dynamic Scoping


lexical scoping

  • if g is defined in f then g inherits f’s bindings
  • the environment is defined by static code structure
  • what you see in most PLs (C, C++, SML, Java, …)

example

playground

fn main() {
    let x = "A";
    let foo = || x;
    let bar = || {
        let x = "B";
        foo()
    };
    println!("x = {}", bar());
}

prints x = "A"


dynamic scoping

  • if f calls g then g inherits f’s bindings
  • the environment is defined by calls
  • the scope of bindings is determined dynamically

example

(set 'x '10)

(defun foo (x) (bar))

(defun bar () (print x))

(foo '5)
; 5

note that 5 is printed and not 10

representation

  • local bindings are represented by a dictionary
  • dictionary maps names to entities
  • the environment is represented by a back-pointer to the most recent stack frame
  • basically a stack of dictionaries
  • search for a name is carried out by searching in the stack of dictionaries

question 1

write a program that prints “DYN” if the language uses dynamic scoping and “LEX” if it uses lexical scoping.

...
const char *x = "LEX";

const char *foo() { return x; }

int main() {
    const char *x = "DYN";
    printf(foo());
}

question 2

can a language with static typing use dynamic scoping?

answer - it’s possible but very awkward making it an unlikely combination

fun foo () = x;
fun bar () = x + 5;
fun baz (x: float) = bar ();
  • what’s the type of foo?
  • what’s the type of bar?
  • should baz pass typechecking?