Open In App

Lexical Scoping in R Programming

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

Lexical Scoping in R programming means that the values of the free variables are searched for in the environment in which the function was defined. An environment is a collection of symbols, value, and pair, every environment has a parent environment it is possible for an environment to have multiple children but the only environment without the parent is the empty environment. If the value of the symbol is not found in the environment in which the function was defined then the search is continued in the parent environment. In R, the free variable bindings are resolve by first looking in the environment in which the function was created. This is called Lexical Scoping.

Why Lexical Scoping?

Lexical Scoping is a set of rules that helps to determine how R represents the value of a symbol. It is an in-built rule in R which automatically works at the language level. It is mostly used to specify statistical calculations. Lexical scoping looks up to symbol based on how functions were nested initially when they were created and not on how they were nested when they called upon. When we use lexical scoping we don’t have to know how the function is called and to figure out where the value of the variable will be looked upon. We only have to look at the function’s definition.

Lexical meaning in lexical scoping is completely different from usual English definition which means relating to words or vocabulary of a language which is distinguished from grammar and construction rather it comes from computer science term lexing meaning by which process that converts code represented as text to meaningful pieces which is comprehensible by programming language. Consider the following example:




f <- function(x, y)
{
  x * y * z
}


In this:

  • x and y are formal arguments
  • z as the free variable

Therefore, the scoping rules of the language determine how values are assigned to free variables. Free variables are not formal arguments and not local variables that are assigned inside of the function body.

Principles of Lexical Scoping

There are four basic principles behind R’s implementation of lexical scoping:

  1. Name Masking
  2. Functions vs variables
  3. A fresh start
  4. Dynamic Lookup

Let’s discuss each principle one by one.

Name Masking

The following example illustrates the most basic principle of lexical scoping, and you should have no problem predicting the output.

  • If variable is not defined inside the function:
    Example:




    c <- 10
    f <- function(a, b)
    {
      a + b + c
    }
    f(8, 5

    
    

    Output:

    [1] 23
    

    It takes the c value as 10 and then adds these numbers and finally we are having 23 as output.

  • If name is not defined inside the function:
    If a name isn’t defined inside a function, R will look one level up.
    Example:




    a <- 10
    b <- function()
    {
      c <- 11
      c(a, c)
    }
    b() 

    
    

    Output:

    [1] 10 11
    
  • When one function is defined inside another function:
    The same rules apply if a function is defined inside another function: look inside the current function, then where that function was defined, and so on, all the way up to the global environment, and then on to other loaded packages.
    Example:




    a <- 10
    g <- function(){
      b <- 20
      h <- function(){
        c <- 30
        c(a, b, c) 
      }
      h()
    }
    g()

    
    

    Output:

    [1] 10 20 30
    
  • When functions are created by another function:
    The same rules apply to closures, functions created by other functions.
    Example:




    a <- function(z){
      b <- 10
      function(){
        z + 4 * b
      }
    }
    x <- a(10)
    x() 

    
    

    Output:

    [1] 50
    

    R returns the accurate value of b after calling the function because x preserves the environment in which it was defined. The environment includes the value of b.

Functions vs Variables

The same principles apply regardless of the type of associated value — finding functions works exactly the same way as finding variables:
Example:




a <- function(x) 10 * x
b <- function(){
  a <- function(x) x + 10
  a(12)
}
b() 


Output:

[1] 22

A Fresh Start

When a function is called, a new environment is created every time. Each acknowledgement is completely independent because a function cannot tell what happened when it was run last time.
Example:




a <- function(){
  if(!exists("z"))
  {
    z <- 10
  }
  else
  {
    z <- z+10
  }
  z
}
a()


Output:

[1] 10

Dynamic Lookup

Lexical scoping controls where to look for values not when to look for them. R looks for the values when the function is executed not when it is created. The output of the function can be different depending on objects outside its environment.
Example:




g <- function() x^3
x <- 10
g() 


Output:

[1] 1000

There is a function in R which is findGlobals() from codetools and it helps us to find all global variables being used in a function and lists all the external dependencies of a function. findGlobals() find the global variables and functions which are used by the closure.
Example:




aGlobal <- rnorm(10)
bGlobal <- rnorm(10)
  
f <- function()
  a <- aGlobal
  b <- bGlobal
  plot(b ~ a)
}
codetools::findGlobals(f) 


Output:

[1] "{"       "~"       "<-"      "aGlobal" "bGlobal" "plot"

We can manually change the environment to the empty environment emptyenv(). emptyenv() is a totally empty environment.



Last Updated : 28 Jul, 2020
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads