lexical scoping (static scoping)
What is lexical scoping (static scoping)?
Lexical scoping, also known as static scoping, is a convention used with many modern programming languages. It refers to setting the scope, or range of functionality, of a variable so that it may be called (referenced) from within the block of code in which it is defined. The scope is determined when the code is compiled. A variable declared in this fashion is sometimes called a private variable.
In a programming language, scope refers to the area where a function or variable is visible and accessible to other code. Below are some common terms associated with scope:
- Global scope refers to a global or public space.
- Local scope refers to a local or restricted region.
- Scope chain refers to the unique spaces that exist from the scope where a variable was called to the global scope.
Understanding lexical scoping
In a programming language, an item's lexical scope is the place in which it was created. The scope of the variable is determined by the program's textual (lexical) structure. Variables can be declared within a specific scope and are only accessible within that region.
In other words, lexical scope refers to the ability of a function scope to access variables from the parent scope. When there is lexical scope, the innermost, inner and outermost functions may access all variables from their parent scopes all the way up to the global scope. However, no scope may access the variables from the functions defined inside it. Thus, the child function is lexically bound to the parent function.
Most modern programming languages involve lexical scoping, including the following:
Other algorithmic languages also standardize lexical scoping, including Ada, pascal, and Modula-2.
Lexical scoping with an example
To understand lexical scoping, here is an example of a code in R. R is an Open Source programming language that's widely used in Windows, Linux and macOS systems. As with other languages, the scoping rules in R determine how values will be associated with the free variable in a function.
This is how lexical scoping can be depicted in R.
#Assign a value to a a <- 7 #Defining function b and c b <- function() a c <- function(){ a < - 8 b() } # Call to function c c()
In this example, a mapping for a is created. Then, a function b is defined, which returns a value of a. In line 3, the function c is defined, creating a new mapping for a and then calling b. Finally, the output value is returned.
Lexical scoping in JavaScript
JavaScript uses lexical scoping to resolve the variable names when a function is created inside another function. It determines the function's parent scope by looking at where the function was created instead of where it was invoked.
Since the language allows functions to be passed around, a programmer can invoke them in a different scope, which breaks its connection to its parent scope. JavaScript solves this problem by creating closures. A closure allows a function to stay connected with its parent scope since it contains both the function and a reference to its parent scope.
JavaScript uses a chain of traversed scopes -- the scope chain -- to find the variables accessible in a certain scope. It will look for a referred variable in the current scope and continue on to the parent scopes until it reaches the global scope and finds the variable.
Here's an example of how lexical scoping works in JavaScript.
var a = 10 var func = function (){ var b = 20; console.log("a and b is accessible (outer):", a, b); var innerFunc= function (){ var c = 30; console.log("a and b and c is accessible (inner):", a, b, c); } innerFunc(); return; } func(); console.log("only a is accessible (global):", a);
In this code, a is a variable whose value can be accessed by all function scopes since it is in the global scope. However, variable b is of local scope for the function assigned to variable func and is therefore not accessible outside the function assigned to func. However, the inner functions are lexically bound by the outer functions, which is why the function assigned to the innerFunc variable can access both variables b and c.
Dynamic scoping
Dynamic scoping is considered the opposite approach of lexical scoping. It creates variables that can be called from outside the block of code in which they are defined. A variable declared in this fashion is sometimes called a public variable.
In dynamic scoping, a variable takes the value of the latest value assigned to it. So, whenever a new function is executed, a new scope is pushed onto the stack. A global identifier refers to the identifier associated with the most recent environment so that the compiler first searches the current block and then successively searches all the calling functions.
Unlike lexical scoping, dynamic scoping is not common in modern languages. However, dynamically scoped languages do exist, such as the original versions of Lisp, bash and LaTeX. Perl and Common Lisp also allow programmers to select dynamic scoping although they are lexically scoped by default.
This will help to understand dynamic scoping with the same example shown earlier in R.
#Assign a value to a a <- 7 #Defining function b and c b <- function() a c <- function() { a < - 8 b() } # Call to function c c()
Both lexical scoping and dynamic scoping are ways in which programming languages can determine the parent scope of a function. See the table above for differences between these methods.