Definition
The strict definition of the (lexical) "scope" of a name ( identifier) is unambiguous: lexical scope is "the portion of source code in which a binding of a name with an entity applies". This is virtually unchanged from its 1960 definition in the specification of ALGOL 60. Representative language specifications follow: ; ALGOL 60 (1960)"Report on the Algorithmic Language Algol 60", 2.7. Quantities, kinds and scopes : The following kinds of quantities are distinguished: simple variables, arrays, labels, switches, and procedures. The scope of a quantity is the set of statements and expressions in which the declaration of the identifier associated with that quantity is valid. ; C (2007) : An identifier can denote an object; a function; a tag or a member of a structure, union, or enumeration; a typedef name; a label name; a macro name; or a macro parameter. The same identifier can denote different entities at different points in the program. ..For each different entity that an identifier designates, the identifier is ''visible'' (i.e., can be used) only within a region of program text called its ''scope.'' ; Go (2013)The Go Programming Language SpecificationLexical scope vs. dynamic scope
A fundamental distinction in scope is what "part of a program" means. In languages with lexical scope (also called static scope), name resolution depends on the location in the source code and the ''lexical context'' (also called ''static context''), which is defined by where the named variable or function is defined. In contrast, in languages with dynamic scope the name resolution depends upon the program state when the name is encountered which is determined by the ''execution context'' (also called ''runtime context'', ''calling context'' or ''dynamic context''). In practice, with lexical scope a name is resolved by searching the local lexical context, then if that fails, by searching the outer lexical context, and so on; whereas with dynamic scope, a name is resolved by searching the local execution context, then if that fails, by searching the outer execution context, and so on, progressing up the call stack. Most modern languages use lexical scope for variables and functions, though dynamic scope is used in some languages, notably some dialects of Lisp, some "scripting" languages, and some template languages. Perl 5 offers both lexical and dynamic scope. Even in lexically scoped languages, scope for closures can be confusing to the uninitiated, as these depend on the lexical context where the closure is defined, not where it is called. Lexical resolution can be determined at compile time, and is also known as ''early binding'', while dynamic resolution can in general only be determined at run time, and thus is known as ''late binding''.Related concepts
InUse
Scope is an important component of name resolution, which is in turn fundamental to language semantics. Name resolution (including scope) varies between programming languages, and within a programming language, varies by type of entity; the rules for scope are called scope rules (or scoping rules). Together with namespaces, scope rules are crucial in modular programming, so a change in one part of the program does not break an unrelated part.Overview
When discussing scope, there are three basic concepts: ''scope,'' ''extent,'' and ''context.'' "Scope" and "context" in particular are frequently confused: scope is a property of a name binding, while context is a property of a part of a program, that is either a portion of source code (''lexical context'' or ''static context'') or a portion of run time (''execution context,'' ''runtime context,'' ''calling context'' or ''dynamic context''). Execution context consists of lexical context (at the current execution point) plus additional runtime state such as the call stack. Strictly speaking, during execution a program enters and exits various name bindings' scopes, and at a point in execution name bindings are "in context" or "not in context", hence name bindings "come into context" or "go out of context" as the program execution enters or exits the scope. However, in practice usage is much looser. Scope is a source-code level concept, and a property of name bindings, particularly variable or function name bindings—names in the source code are references to entities in the program—and is part of the behavior of a compiler or interpreter of a language. As such, issues of scope are similar to pointers, which are a type of reference used in programs more generally. Using the value of a variable when the name is in context but the variable is uninitialized is analogous to dereferencing (accessing the value of) a wild pointer, as it is undefined. However, as variables are not destroyed until they go out of context, the analog of a dangling pointer does not exist. For entities such as variables, scope is a subset ofglobal
and nonlocal
keywords in Python; in other cases the default rules cannot be overridden.
When two identical names are in context at the same time, referring to different entities, one says that '' name masking'' is occurring, where the higher-priority name (usually innermost) is "masking" the lower-priority name. At the level of variables, this is known as variable shadowing. Due to the potential for logic errors from masking, some languages disallow or discourage masking, raising an error or warning at compile time or run time.
Various programming languages have various different scope rules for different kinds of declarations and names. Such scope rules have a large effect on language semantics and, consequently, on the behavior and correctness of programs. In languages like C++, accessing an unbound variable does not have well-defined semantics and may result in undefined behavior, similar to referring to a dangling pointer; and declarations or names used outside their scope will generate syntax errors.
Scopes are frequently tied to other language constructs and determined implicitly, but many languages also offer constructs specifically for controlling scope.
Levels of scope
Scope can vary from as little as a single expression to as much as the entire program, with many possible gradations in between. The simplest scope rule is global scope—all entities are visible throughout the entire program. The most basic modular scope rule is two-level scope, with a global scope anywhere in the program, and local scope within a function. More sophisticated modular programming allows a separate module scope, where names are visible within the module (private to the module) but not visible outside it. Within a function, some languages, such as C, allow block scope to restrict scope to a subset of a function; others, notably functional languages, allow expression scope, to restrict scope to a single expression. Other scopes include file scope (notably in C) which behaves similarly to module scope, and block scope outside of functions (notably in Perl). A subtle issue is exactly when a scope begins and ends. In some languages, such as C, a name's scope begins at the name declaration, and thus different names declared within a given block can have different scopes. This requires declaring functions before use, though not necessarily defining them, and requires forward declaration in some cases, notably for mutual recursion. In other languages, such as Python, a name's scope begins at the start of the relevant block where the name is declared (such as the start of a function), regardless of where it is defined, so all names within a given block have the same scope. In JavaScript, the scope of a name declared withlet
or const
begins at the name declaration, and the scope of a name declared with var
begins at the start of the function where the name is declared, which is known as '' variable hoisting''. Behavior of names in context that have undefined value differs: in Python use of undefined names yields a runtime error, while in JavaScript undefined names declared with var
are usable throughout the function because they are implicitly bound to the value undefined
.
Expression scope
The scope of a name binding is an expression, which is known as expression scope. Expression scope is available in many languages, especially functional languages which offer a feature called ''let-expressions'' allowing a declaration's scope to be a single expression. This is convenient if, for example, an intermediate value is needed for a computation. For example, in Standard ML, iff()
returns 12
, then let val x = f() in x * x end
is an expression that evaluates to 144
, using a temporary variable named x
to avoid calling f()
twice. Some languages with block scope approximate this functionality by offering syntax for a block to be embedded into an expression; for example, the aforementioned Standard ML expression could be written in Block scope
The scope of a name binding is a block, which is known as block scope. Block scope is available in many, but not all, block-structured programming languages. This began with ALGOL 60, where " ery declaration ... is valid only for that block.", and today is particularly associated with languages in the Pascal and C families and traditions. Most often this block is contained within a function, thus restricting the scope to a part of a function, but in some cases, such as Perl, the block may not be within a function.n * n
would generally not be assigned to an auxiliary variable, and the body of the loop would simply be written ret += n * n
but in more complicated examples auxiliary variables are useful.
Blocks are primarily used for control flow, such as with if, while, and for loops, and in these cases block scope means the scope of variable depends on the structure of a function's flow of execution. However, languages with block scope typically also allow the use of "naked" blocks, whose sole purpose is to allow fine-grained control of variable scope. For example, an auxiliary variable may be defined in a block, then used (say, added to a variable with function scope) and discarded when the block ends, or a while loop might be enclosed in a block that initializes variables used inside the loop that should only be initialized once.
A subtlety of several programming languages, such as a
is accessible after the if
statement.
In Perl, which has block scope, this instead requires declaring the variable prior to the block:
$counter
is a variable name with block scope (due to the use of the my
keyword), while increment_counter
is a function name with global scope. Each call to increment_counter
will increase the value of $counter
by one, and return the new value. Code outside of this block can call increment_counter
, but cannot otherwise obtain or alter the value of $counter
. This idiom allows one to define closures in Perl.
Function scope
When the scope of variables declared within a function does not extend beyond that function, this is known as function scope. Function scope is available in most programming languages which offer a way to create a '' local variable'' in a function or subroutine: a variable whose scope ends (that goes out of context) when the function returns. In most cases the lifetime of the variable is the duration of the function call—it is an automatic variable, created when the function starts (or the variable is declared), destroyed when the function returns—while the scope of the variable is within the function, though the meaning of "within" depends on whether scope is lexical or dynamic. However, some languages, such as C, also provide forsquare
and sum_of_squares
. square
computes the square of a number; sum_of_squares
computes the sum of all squares up to a number. (For example, square(4)
is 42 = 16
, and sum_of_squares(4)
is 02 + 12 + 22 + 32 + 42 = 30
.)
Each of these functions has a variable named n that represents the argument to the function. These two n variables are completely separate and unrelated, despite having the same name, because they are lexically scoped local variables with function scope: each one's scope is its own, lexically separate function and thus, they don't overlap. Therefore, sum_of_squares
can call square
without its own n being altered. Similarly, sum_of_squares
has variables named total and i; these variables, because of their limited scope, will not interfere with any variables named total or i that might belong to any other function. In other words, there is no risk of a ''name collision'' between these names and any unrelated names, even if they are identical.
No name masking is occurring: only one variable named n is in context at any given time, as the scopes do not overlap. By contrast, were a similar fragment to be written in a language with dynamic scope, the n in the calling function would remain in context in the called function—the scopes would overlap—and would be masked ("shadowed") by the new n in the called function.
Function scope is significantly more complicated if functions are first-class objects and can be created locally to a function and then returned. In this case any variables in the nested function that are not local to it (unbound variables in the function definition, that resolve to variables in an enclosing context) create a closure, as not only the function itself, but also its context (of variables) must be returned, and then potentially called in a different context. This requires significantly more support from the compiler, and can complicate program analysis.
File scope
The scope of a name binding is a file, which is known as file scope. File scope is largely particular to C (and C++), where scope of variables and functions declared at the top level of a file (not within any function) is for the entire file—or rather for C, from the declaration until the end of the source file, or more precisely translation unit (internal linking). This can be seen as a form of module scope, where modules are identified with files, and in more modern languages is replaced by an explicit module scope. Due to the presence of include statements, which add variables and functions to the internal context and may themselves call further include statements, it can be difficult to determine what is in context in the body of a file. In the C code snippet above, the function namesum_of_squares
has file scope.
Module scope
The scope of a name binding is a module, which is known as module scope. Module scope is available in modular programming languages where modules (which may span various files) are the basic unit of a complex program, as they allow information hiding and exposing a limited interface. Module scope was pioneered in theGlobal scope
The scope of a name binding is an entire program, which is known as global scope. Variable names with global scope—called '' global variables''—are frequently considered bad practice, at least in some languages, due to the possibility of name collisions and unintentional masking, together with poor modularity, and function scope or block scope are considered preferable. However, global scope is typically used (depending on the language) for various other sorts of names, such as names of functions, names of classes and names of other data types. In these cases mechanisms such as namespaces are used to avoid collisions.Lexical scope vs. dynamic scope
The use of local variables — of variable names with limited scope, that only exist within a specific function — helps avoid the risk of a name collision between two identically named variables. However, there are two very different approaches to answering this question: What does it mean to be "within" a function? In lexical scope (or lexical scoping; also called static scope or static scoping), if a variable name's scope is a certain function, then its scope is the program text of the function definition: within that text, the variable name exists, and is bound to the variable's value, but outside that text, the variable name does not exist. By contrast, in dynamic scope (or dynamic scoping), if a variable name's scope is a certain function, then its scope is the time-period during which the function is executing: while the function is running, the variable name exists, and is bound to its value, but after the function returns, the variable name does not exist. This means that if functionf
invokes a separately defined function g
, then under lexical scope, function g
does ''not'' have access to f
's local variables (assuming the text of g
is not inside the text of f
), while under dynamic scope, function g
''does'' have access to f
's local variables (since g
is invoked during the invocation of f
).
x
and initializes it to 1
. The second line, g
that prints out ("echoes") the current value of x
, and then sets x
to 2
(overwriting the previous value). The third line, f
that creates a local variable x
(hiding the identically named global variable) and initializes it to 3
, and then calls g
. The fourth line, f
. The fifth line, x
.
So, what exactly does this program print? It depends on the scope rules. If the language of this program is one that uses lexical scope, then g
prints and modifies the global variable x
(because g
is defined outside f
), so the program prints 1
and then 2
. By contrast, if this language uses dynamic scope, then g
prints and modifies f
's local variable x
(because g
is called from within f
), so the program prints 3
and then 1
. (As it happens, the language of the program is Bash, which uses dynamic scope; so the program prints 3
and then 1
. If the same code was run with ksh93 which uses lexical scope, the results would be different.)
Lexical scope
With lexical scope, a name always refers to its lexical context. This is a property of the program text and is made independent of the runtime call stack by the language implementation. Because this matching only requires analysis of the static program text, this type of scope is also called static scope. Lexical scope is standard in allI
is visible at all points, because it is never hidden by another variable of the same name. The char
variable K
is visible only in the main program because it is hidden by the real
variable K
visible in procedure B
and C
only. Variable L
is also visible only in procedure B
and C
but it does not hide any other variable. Variable M
is only visible in procedure C
and therefore not accessible either from procedure B
or the main program. Also, procedure C
is visible only in procedure B
and can therefore not be called from the main program.
There could have been another procedure C
declared in the program outside of procedure B
. The place in the program where "C
" is mentioned then determines which of the two procedures named C
it represents, thus precisely analogous with the scope of variables.
Correct implementation of lexical scope in languages with first-class nested functions is not trivial, as it requires each function value to carry with it a record of the values of the variables that it depends on (the pair of the function and this context is called a closure). Depending on implementation and History
Lexical scope was first used in the early 1960s for the imperative language ALGOL 60 and has been picked up in most other imperative languages since then.Borning AMost LISP implementations are internally inconsistent in that by default the interpreter and compiler may assign different semantics to correct programs; this stems primarily from the fact that the interpreter assumes all variables to be dynamically scoped, while the compiler assumes all variables to be local unless forced to assume otherwise. This has been done for the sake of convenience and efficiency, but can lead to very subtle bugs. The definition of Common LISP avoids such anomalies by explicitly requiring the interpreter and compiler to impose identical semantics on correct programs.Implementations of Common LISP were thus required to have lexical scope. Again, from ''An overview of Common LISP'':
In addition, Common LISP offers the following facilities (most of which are borrowed from MacLisp, InterLisp or Lisp Machines Lisp): (...) Fully lexically scoped variables. The so-called "FUNARG problem" is completely solved, in both the downward and upward cases.By the same year in which ''An overview of Common LISP'' was published (1982), initial designs (also by Guy L. Steele Jr.) of a compiled, lexically scoped Lisp, called '' Scheme'' had been published and compiler implementations were being attempted. At that time, lexical scope in Lisp was commonly feared to be inefficient to implement. In ''A History of T'', Olin Shivers writes:
''All'' serious Lisps in production use at that time were dynamically scoped. No one who hadn't carefully read the Rabbit thesis (written by Guy Lewis Steele Jr. in 1978) believed lexical scope would fly; even the few people who ''had'' read it were taking a bit of a leap of faith that this was going to work in serious production use.The term "lexical scope" dates at least to 1967, while the term "lexical scoping" dates at least to 1970, where it was used in Project MAC to describe the scope rules of the Lisp dialect MDL (then known as "Muddle").
Dynamic scope
With dynamic scope, a name refers to execution context. In technical terms, this means that each name has a global stack of bindings. Introducing a local variable with namex
pushes a binding onto the global x
stack (which may have been empty), which is popped off when the x
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, which is why this type of scope is called ''dynamic'' scope.
Dynamic scope is uncommon in modern languages.
Generally, certain blocks 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 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:a
to the passed variable, with this name only later resolved by the compiler based on where the macro ADD_A
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 a
in the macro is resolved (after expansion) to the local variable at the expansion site:
Qualified names
As we have seen, one of the key reasons for scope is that it helps prevent name collisions, by allowing identical names to refer to distinct things, with the restriction that the names must have separate scopes. Sometimes this restriction is inconvenient; when many different things need to be accessible throughout a program, they generally all need names with global scope, so different techniques are required to avoid name collisions. To address this, many languages offer mechanisms for organizing global names. The details of these mechanisms, and the terms used, depend on the language; but the general idea is that a group of names can itself be given a name — a prefix — and, when necessary, an entity can be referred to by a ''qualified name'' consisting of the name plus the prefix. Normally such names will have, in a sense, two sets of scopes: a scope (usually the global scope) in which the qualified name is visible, and one or more narrower scopes in which the ''unqualified name'' (without the prefix) is visible as well. And normally these groups can themselves be organized into groups; that is, they can be ''nested''. Although many languages support this concept, the details vary greatly. Some languages have mechanisms, such as ''namespaces'' in C++ and C#, that serve almost exclusively to enable global names to be organized into groups. Other languages have mechanisms, such as ''packages'' in Ada and ''structures'' in Standard ML, that combine this with the additional purpose of allowing some names to be visible only to other members of their group. And object-oriented languages often allow classes or singleton objects to fulfill this purpose (whether or not they ''also'' have a mechanism for which this is the primary purpose). Furthermore, languages often meld these approaches; for example,By language
Scope rules for representative languages follow.C
In C, scope is traditionally known as linkage or visibility, particularly for variables. C is a lexically scoped language with global scope (known as ''external linkage''), a form of module scope or file scope (known as ''internal linkage''), and local scope (within a function); within a function scopes can further be nested via block scope. However, standard C does not support nested functions. The lifetime and visibility of a variable are determined by its storage class. There are three types of lifetimes in C: static (program execution), automatic (block execution, allocated on the stack), and manual (allocated on the heap). Only static and automatic are supported for variables and handled by the compiler, while manually allocated memory must be tracked manually across different variables. There are three levels of visibility in C: external linkage (global), internal linkage (roughly file), and block scope (which includes functions); block scopes can be nested, and different levels of internal linkage is possible by use of includes. Internal linkage in C is visibility at the translation unit level, namely a source file after being processed by the C preprocessor, notably including all relevant includes. C programs are compiled as separate object files, which are then linked into an executable or library via a linker. Thus name resolution is split across the compiler, which resolves names within a translation unit (more loosely, "compilation unit", but this is properly a different concept), and the linker, which resolves names across translation units; see linkage for further discussion. In C, variables with block scope enter context when they are declared (not at the top of the block), go out of context if any (non-nested) function is called within the block, come back into context when the function returns, and go out of context at the end of the block. In the case of automatic local variables, they are also allocated on declaration and deallocated at the end of the block, while for static local variables, they are allocated at program initialization and deallocated at program termination. The following program demonstrates a variable with block scope coming into context partway through the block, then exiting context (and in fact being deallocated) when the block ends:m m b mThere are other levels of scope in C. Variable names used in a function prototype have function prototype visibility, and exit context at the end of the function prototype. Since the name is not used, this is not useful for compilation, but may be useful for documentation. Label names for GOTO statement have function scope, while case label names for switch statements have block scope (the block of the switch).
C++
All the variables that we intend to use in a program must have been declared with its type specifier in an earlier point in the code, like we did in the previous code at the beginning of the body of the function main when we declared that a, b, and result were of type int. A variable can be either of global or local scope. A global variable is a variable declared in the main body of the source code, outside all functions, while a local variable is one declared within the body of a function or a block. Modern versions allow nested lexical scope.Swift
Swift has a similar rule for scopes with C++, but contains different access modifiers.Go
Go is lexically scoped using blocks.Java
JavaScript
let
and const
keywords is standard since ECMAScript 6. Block scope can be produced by wrapping the entire block in a function and then executing it; this is known as the immediately-invoked function expression (IIFE) pattern.
While JavaScript scope is simple—lexical, function-level—the associated initialization and name resolution rules are a cause of confusion. Firstly, assignment to a name not in scope defaults to creating a new global variable, not a local one. Secondly, to create a new local variable one must use the var
keyword; the variable is then created at the top of the function, with value undefined
and the variable is assigned its value when the assignment expression is reached:
:A variable with an ''Initialiser'' is assigned the value of its ''AssignmentExpression'' when the ''VariableStatement'' is executed, not when the variable is created.
This is known as '' variable hoisting''—the declaration, but not the initialization, is hoisted to the top of the function. Thirdly, accessing variables before initialization yields undefined
, rather than a syntax error. Fourthly, for function declarations, the declaration and the initialization are both hoisted to the top of the function, unlike for variable initialization. For example, the following code produces a dialog with output undefined, as the local variable declaration is hoisted, shadowing the global variable, but the initialization is not, so the variable is undefined when used:
Lisp
SPECIAL
declarations for particular variables. However, Maclisp treated lexical binding more as an optimization than one would expect in modern languages, and it did not come with the closure feature one might expect of lexical scope in modern Lisps. A separate operation, *FUNCTION
, was available to somewhat clumsily work around some of that issue.
Common Lisp adopted lexical scope from Scheme, as did Clojure.
ISLISP has lexical scope for ordinary variables. It also has dynamic variables, but they are in all cases explicitly marked; they must be defined by a defdynamic
special form, bound by a dynamic-let
special form, and accessed by an explicit dynamic
special form.
Some other dialects of Lisp, like Emacs Lisp, still use dynamic scope by default. Emacs Lisp now has lexical scope available on a per-buffer basis.
Python
For variables, Python has function scope, module scope, and global scope. Names enter context at the start of a scope (function, module, or global scope), and exit context when a non-nested function is called or the scope ends. If a name is used prior to variable initialization, this raises a runtime exception. If a variable is simply accessed (not assigned to), name resolution follows the LEGB (Local, Enclosing, Global, Built-in) rule which resolves names to the narrowest relevant context. However, if a variable is assigned to, it defaults to declaring a variable whose scope starts at the start of the level (function, module, or global), not at the assignment. Both these rules can be overridden with aglobal
or nonlocal
(in Python 3) declaration prior to use, which allows accessing global variables even if there is a masking nonlocal variable, and assigning to global or nonlocal variables.
As a simple example, a function resolves a variable to the global scope:
x
is defined before f
is called, so no error is raised, even though it is defined after its reference in the definition of f
. Lexically this is a forward reference, which is allowed in Python.
Here assignment creates a new local variable, which does not change the value of the global variable:
global
or nonlocal
(in Python 3) keywords. In the below code, the global x
declaration in g
means that x
resolves to the global variable. It thus can be accessed (as it has already been defined), and assignment assigns to the global variable, rather than declaring a new local variable. Note that no global
declaration is needed in f
—since it does not assign to the variable, it defaults to resolving to the global variable.
global
can also be used for nested functions. In addition to allowing assignment to a global variable, as in an unnested function, this can also be used to access the global variable in the presence of a nonlocal variable:
nonlocal
declaration, for assigning to a nonlocal variable, similar to using global
in an unnested function:
R
R is a lexically scoped language, unlike other implementations of S where the values of free variables are determined by a set of global variables, while in R they are determined by the context in which the function was created. The scope contexts may be accessed using a variety of features (such asparent.frame()
) which can simulate the experience of dynamic scope should the programmer desire.
There is no block scope:
See also
* Closure (computer science) * Global variable * Local variable * Let expression *Notes
References
* *