Lexical & Dynamic Scope
Aug 9 2016
Take this snippet of pseudo-code
var a = 1
defn g()
print a
defn h
var a = 10
print a
defn f()
var a = 2
g()
If we were to call g
, what value would print out? Would it print out at all? Would it raise an exception?
The answer to these questions depends on the scoping rules of this pseudo-language. Most of us would assume that calling g
would print out 1, and there is a good reason for that assumption.
>>> g()
#=> 1
We are going to talk about two different scoping rules: lexical scope and dynamic scope.
Lexical Scope
Lexical scope is where name resolution is based on the textual representation of the code. Put another way, name resolution is dependent on the context within the source code.
Let’s go back to the pseudo-code and walk through a variable look-up as if the language was lexically scoped. The question we are trying to answer here is how did the language decide that a
is 1
.
Here are the steps that are taken if the language is lexically scoped.
First, we look to see if the name is defined locally, within the function (usually this means defined prior to the reference, but not always. Search for “JavaScript variable hoisting”)
No such name exists, so we refer to the enclosing context where the function was defined in, which, in this case, is the global context.
Within the global context, we find a name
a
, and we use that binding in our call toprint
.
>>> g()
#=> 1
What would the function h
print?
>>> h()
#=> 10
h
would print 10
, because a
is defined locally. A local definition trumps all others.
What would print out if f
was called?
>>> f()
#=> 1
It would still print out 1.
Even though a
is defined inside of f
before g
is called, g
resolves the name based on where the function was was defined (lexically), instead of where the function was called. Thus, the same resolution steps are taken as when we called g
outside of f
.
So our initial assumption was correct. This is because most moderns languages use lexical scoping.
Dynamic Scope
In our last example, when we called f
, what if, instead of looking for the variable a
based on where the called function is defined, we looked for the variable based on where it is called?
What?
Put another way, what if we changed the scoping rules so that we when we called f
, g
used the version of the variable a
that was defined in the body of f
?
>>> f()
#=> 2
This is known as dynamic scoping. Instead of resolving names based on where the function is defined, names are resolved based on the invoking context.
Conclusion
Likely, you are already comfortable with lexical scoping, even if you didn’t know what it meant before reading this post. And in this example we only resolved variable names, but this applies to any other binding too, such as functions.
Dynamic scoping is not nearly as common. Some early Lisps used dynamic scoping, and one Lisp continues to use dynamic scoping, which is Emacs Lisp, the language used to script the Emacs editor1.
It should be also be noted that most shell languages also use dynamic scoping. Here is an example in bash2.
#!/bin/bash
a=1
function g() {
echo $a;
}
function f() {
local a=2;
g
}
f
#=> 2
Example was derived this snippet
Knowing this could save you a headache the next time you need to write a shell script.
Sources
Lexical Scoping. (2011, May 8). In WikiWikiWeb Retrieved August 9, 2016, from http://c2.com/cgi/wiki?LexicalScoping
Scope (computer science). (2016, July 22). In Wikipedia, The Free Encyclopedia. Retrieved August 9, 2016, from https://en.wikipedia.org/w/index.php?title=Scope(computerscience)&oldid=731014985
Recent Articles
- Apprenticeship Retro Oct 14 2016
- What the hell is CORS Oct 13 2016
- Cross-site Tracing Oct 11 2016
- Learning Testing Tools First Oct 7 2016