Open main menu
Home
Random
Recent changes
Special pages
Community portal
Preferences
About Wikipedia
Disclaimers
Incubator escapee wiki
Search
User menu
Talk
Dark mode
Contributions
Create account
Log in
Editing
Scope (computer science)
(section)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
== Dynamic scope == With '''dynamic scope''', a name refers to execution context. In technical terms, this means that each name has a global [[Stack (data structure)|stack]] of bindings. Introducing a local variable with name <code>x</code> pushes a binding onto the global <code>x</code> stack (which may have been empty), which is popped off when the [[control flow]] leaves the scope. Evaluating <code>x</code> in any context always yields the top binding. Note that this cannot be done at compile-time because the binding stack only exists at [[Run time (program lifecycle phase)|run-time]], which is why this type of scope is called ''dynamic'' scope. Dynamic scope is uncommon in modern languages.<ref name=Borning /> Generally, certain [[Block (programming)|block]]s are defined to create bindings whose lifetime is the execution time of the block; this adds some features of static scope to the dynamic scope process. However, since a section of code can be called from many different locations and situations, it can be difficult to determine at the outset what bindings will apply when a variable is used (or if one exists at all). This can be beneficial; application of the [[principle of least knowledge]] suggests that code avoid depending on the ''reasons'' for (or circumstances of) a variable's value, but simply use the value according to the variable's definition. This narrow interpretation of shared data can provide a very flexible system for adapting the behavior of a function to the current state (or policy) of the system. However, this benefit relies on careful documentation of all variables used this way as well as on careful avoidance of assumptions about a variable's behavior, and does not provide any mechanism to detect interference between different parts of a program. Some languages, like [[Perl]] and [[Common Lisp]], allow the programmer to choose static or dynamic scope when defining or redefining a variable. Examples of languages that use dynamic scope include [[Logo (programming language)|Logo]], [[Emacs Lisp]], [[LaTeX]] and the shell languages [[Bash (Unix shell)|bash]], [[Debian Almquist shell|dash]], and [[Windows PowerShell|PowerShell]]. Dynamic scope is fairly easy to implement. To find an name's value, the program could traverse the runtime stack, checking each activation record (each function's stack frame) for a value for the name. In practice, this is made more efficient via the use of an [[association list]], which is a stack of name/value pairs. Pairs are pushed onto this stack whenever declarations are made, and popped whenever variables go out of context.{{sfn|Scott|2009|loc=3.4 Implementing Scope, p. 143}} ''Shallow binding'' is an alternative strategy that is considerably faster, making use of a ''central reference table'', which associates each name with its own stack of meanings. This avoids a linear search during run-time to find a particular name, but care should be taken to properly maintain this table.{{sfn|Scott|2009|loc=3.4 Implementing Scope, p. 143}} Note that both of these strategies assume a last-in-first-out ([[LIFO (computing)|LIFO]]) ordering to bindings for any one variable; in practice all bindings are so ordered. An even simpler implementation is the representation of dynamic variables with simple global variables. The local binding is performed by saving the original value in an anonymous location on the stack that is invisible to the program. When that binding scope terminates, the original value is restored from this location. In fact, dynamic scope originated in this manner. Early implementations of Lisp used this obvious strategy for implementing local variables, and the practice survives in some dialects which are still in use, such as GNU Emacs Lisp. Lexical scope was introduced into Lisp later. This is equivalent to the above shallow binding scheme, except that the central reference table is simply the global variable binding context, in which the current meaning of the variable is its global value. Maintaining global variables isn't complex. For instance, a symbol object can have a dedicated slot for its global value. Dynamic scope provides an excellent abstraction for [[thread-local storage]], but if it is used that way it cannot be based on saving and restoring a global variable. A possible implementation strategy is for each variable to have a thread-local key. When the variable is accessed, the thread-local key is used to access the thread-local memory location (by code generated by the compiler, which knows which variables are dynamic and which are lexical). If the thread-local key does not exist for the calling thread, then the global location is used. When a variable is locally bound, the prior value is stored in a hidden location on the stack. The thread-local storage is created under the variable's key, and the new value is stored there. Further nested overrides of the variable within that thread simply save and restore this thread-local location. When the initial, outermost override's context terminates, the thread-local key is deleted, exposing the global version of the variable once again to that thread. With [[referential transparency]] the dynamic scope is restricted to the argument stack of the current function only, and coincides with the lexical scope. === Macro expansion === {{main article|Macro expansion}} In modern languages, [[macro expansion]] in a [[preprocessor]] is a key example of de facto dynamic scope. The macro language itself only transforms the source code, without resolving names, but since the expansion is done in place, when the names in the expanded text are then resolved (notably free variables), they are resolved based on where they are expanded (loosely "called"), as if dynamic scope were occurring. The [[C preprocessor]], used for [[macro expansion]], has de facto dynamic scope, as it does not do name resolution by itself and it is independent of where the macro is defined. For example, the macro: <syntaxhighlight lang=c> #define ADD_A(x) x + a </syntaxhighlight> will expand to add <code>a</code> to the passed variable, with this name only later resolved by the compiler based on where the macro <code>ADD_A</code> is "called" (properly, expanded). Properly, the C preprocessor only does [[lexical analysis]], expanding the macro during the tokenization stage, but not parsing into a syntax tree or doing name resolution. For example, in the following code, the name <code>a</code> in the macro is resolved (after expansion) to the local variable at the expansion site: <syntaxhighlight lang=c> #define ADD_A(x) x + a void add_one(int *x) { const int a = 1; *x = ADD_A(*x); } void add_two(int *x) { const int a = 2; *x = ADD_A(*x); } </syntaxhighlight>
Edit summary
(Briefly describe your changes)
By publishing changes, you agree to the
Terms of Use
, and you irrevocably agree to release your contribution under the
CC BY-SA 4.0 License
and the
GFDL
. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.
Cancel
Editing help
(opens in new window)