Dynamic scope exists but it's not seen very frequently nowadays.
Dynamic scope can be explained by analogy with C++'s exception handling.
When an exception is thrown, the computer must
search the call stack for a matching handler.
To emphasize, the set of handlers that are considered depends on the current call stack.
In an environment that employs dynamic scoping:
When a variable is named, the computer must
search the call stack for an variable with a matching name. To emphasize, the set of variables that are considered depends on the current call stack.
For example, consider this program.
1 2 3 4 5 6 7 8 9 10 11
|
#include <iostream>
int x = 1;
void g() { x = 3; }
void f() { int x = 2; g(); std::cout << x << ' '; }
int main()
{
f(); std::cout << x << '\n';
g(); std::cout << x << '\n';
}
| |
Now consider
g. In a lexically-scoped language like C++20, the name
x refers to the nearest
x declared in a textually enclosing scope. That's the global one,
::x, a fact which can be determined by looking at the program's source code.
In a dynamically-scoped language like C++2077, the name
x refers to the first
x that is found by searching the call stack. That is, the callers are checked in order for a matching declaration. So if
f is
g's caller, then the environments of
g,
f, and then finally the global environment are searched in order for a matching declaration.
So if we run this code in C++20, which has lexical scope rules, we get
But if we write code against the upcoming C++2077 standard, which has dynamic scope rules, then we get