Search code examples
schemeprogramming-languageslexical-scope

lexical scoping in scheme


I am trying to understand concepts of lexical and dynamic scoping and the differences between them. Let's take a look at the following code:

(let ((a 1)
      (b 2))
  (letrec ((f (lambda () F))
            (g (lambda (c) G)))
    (lambda (d)
      (+ (f) (g b)))))

For expressions F and G, which variables are at lexical scope of (lambda(d)...)?


Solution

  • (lambda(d)...) has d as bound variable and f, g a b and all of the global scope as free variables.

    EDIT

    Just to demonstrate the code in Scheme and some other language where the same bindings are dynamic. Since neither of your functions call each other you might as well keep them in the same let:

    (define test
      (let ((a 1)
            (b 2)
            (f (lambda () F))
            (g (lambda (c) G)))
        (lambda (d)
          (+ (f) (g b)))))
    
    ;; for the test we need the F and G defined
    (define F 10)
    (define G 20)
    
    (test 4) ; ==> 30 (aka (+ F G))
    

    What happens is that when the lambda gets evaluated the variables it uses from the lexical scope lives on even after the let is gone. In a dymaic scope this isn't true:

    (test 4)
    ; ERROR f: unbound identifier
    

    The reason for this is while a, b, f and g existed when the lamba was evaluated none of the variables get captured like in lexical scope and thus when the procedure test is made none of the local variables exist anymore. In fact you might as well write it like this:

    ;; in a dynamic lisp this s the same 
    (define test
      (lambda (d)
        (+ (f) (g b))))
    

    And you must make sure the variable exist when you call the function (aka dynamic)

    (define g (lambda (v) 1337))
    (define f (lambda () 3.1415927))
    (define b 13)
    (test 4) ; ==> 1340.1415927
    

    Now if you were to add the above definitions in the global scope and keep the original definition you'd still get 30 since a lexical lisp uses the closer lexical bindings rather than the global ones.

    Another great example is this:

    (define x 10)
    (define (test v)
      (+ x v))
    
    (let ((x 20))
      (test 10))
    ; ==> ?
    

    In a lexical lisp the result would be always 20 since test does not have any idea of the let since it is not in its lexical scope. It's x is always the global bindings x. In a dymamic lisp the x from the let is the closest one from the runtime point of view which would result in 30 since the x in test is the same as the x in the let and it shadows the global x.