Scope rules define the visibility rules for names in a
programming language. What if you have references to a variable named
k
in different parts of the program? Do these refer to the
same variable or to different ones?
Languages such as Haskell, Algol, Ada, C, Pascal and Scheme are lexically scoped. A block defines a new scope. Variables can be declared in that scope, and aren't visible from the outside. However, variables outside the scope -- in enclosing scopes -- are visible unless they are overridden (shadowed). In Algol and Pascal (but not C or Ada) these scope rules also apply to the names of functions and procedures.
Lexical scoping is also called static scoping.
We'll use Scheme to write the examples -- but the concept applies in many different languages.
(define m 50) (define n 100) (define (hardy) (display (list "In Hardy, n=" n)) (newline)) (define (laurel n) (display (list "In Laurel, m=" m)) (newline) (display (list "In Laurel, n=" n)) (newline) (hardy)) (display (list "In main program, n=" n)) (newline) (laurel 1) (hardy)Output:
In main program, n = 100 In laurel, m = 50 In laurel, n = 1 In hardy, n = 100 ;; called from laurel in hardy, n = 100 ;; called from main
defvar
(but not in Scheme).
Example:
;; Repeat the example above, but assume ;; that Scheme is dynamically scoped.Output:
In main program, n = 100 In laurel, m = 50 In laurel, n = 1 In hardy, n = 1 ;; <== NOTE!! called from laurel in hardy, n = 100 ;; called from main
Consider the following:
(define (mymap f s) (if (null? s) '() (cons (f (car s)) (mymap f (cdr s))))) (let ((s 10)) (mymap (lambda (r) (+ r s)) '(1 2 3)))
mymap is just like map in Scheme. With lexical scoping the result is (11 12 13). Just right.
But if we use dynamic scoping, we get an error: + complains that it can't add a number to a list!! When we evaluate the lambda, s is bound to the variable s in the definition of mymap.
The Laurel & Hardy example illustrates another aspect of this problem: the behavior of Hardy varies depending on where you call it from. You (and the compiler) can't just look at the code and figure out what the variables are bound to. This makes it both harder to reason about and harder to compile efficiently.
Dynamic scoping has disappeared from recent programming languages. The exception is exception handling! That essentially uses dynamic scoping to associate a handler with an exception.