Lexical Scoping in R Programming

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:

filter_none

edit
close

play_arrow

link
brightness_4
code

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

chevron_right


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:

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

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

    chevron_right

    
    

    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:

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

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

    chevron_right

    
    

    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:

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

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

    chevron_right

    
    

    Output:

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

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

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

    chevron_right

    
    

    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:

filter_none

edit
close

play_arrow

link
brightness_4
code

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

chevron_right


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:

filter_none

edit
close

play_arrow

link
brightness_4
code

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

chevron_right


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:

filter_none

edit
close

play_arrow

link
brightness_4
code

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

chevron_right


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:

filter_none

edit
close

play_arrow

link
brightness_4
code

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

chevron_right


Output:

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

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




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :

1


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.