These concepts are very important for those who develop R packages and need to create complex functions behaviors, although for the day-to-day analysis of data is not so necessary.
First, to understand the concept of lexical scope and dynamic scope, I think the best example is the following:
y <- 100
f <- function(x) {
y <- 2
g(x)
}
g <- function(x) {
x*y
}
f(5)
[1] 500
g(5)
[1] 500
In R, the scope is lexical, that is, the functions look for the values of the variables in the same environment in which they were defined and not in the environment in which they were called. In the example above, if the scope was dynamic then y
would assume the value 2
and the result of f(5)
would be 10
. It is important to note that if you change the value of y
after the creation of the function, R will use the new value. For example:
y <- 10
f(5)
[1] 50
That is, R stores where to find the variable value and not the variable value when the function is created.
What is the use of the lexical scope? As a function always accesses variables that are in the scope in which it was defined, you can create functions of the following type:
counter <- function() {
i <- 0
function() {
i <<- i + 1
return(i)
}
}
count <- counter()
count2 <- counter()
count()
[1] 1
count()
[1] 2
count()
[1] 3
count()
[1] 4
count2()
[1] 1
count2()
[1] 2
count()
[1] 5
On a day-to-day basis, this may not be useful, but in some rare moments you may need to create functions that have complex behaviors like this.
Another common use is to create functions like generators
, an example is this one:
sampling_generator <- function(X_data, Y_data, size) {
function() {
rows <- sample(1:nrow(X_data), size, replace = TRUE)
list(X_data[rows,], Y_data[rows,])
}
}
This function returns a different random sample of X_data and Y_data each time it is called. This allows you to create very interesting abstractions in your code.
In R, it is also possible to modify these functions' scope rules. Modifying these rules can make your function simpler to use. The functions of dplyr
make good use of it, for example when we write:
dplyr::mutate(mtcars, cyl = cyl * 2)
In this case, the lexical scope rule was disregarded since the function did not look for the variable cyl
in the global environment, where it would have been natural and yes within the mtcars
. This code also did not create a variable cyl
within the global environment and yes within the mtcars
.
In this case, modifying the scope rule makes the function user have to type much less which can be very useful when we are doing data analysis.
In summary, in most cases, when using functions defined by other developers we don’t need to worry about lexical or dynamic scope. When creating packages or functions that will be used by other people we can think whether we want the lexical scope or modify the scope rules to improve the functions API. We may also abuse these scope rules to create more advanced behaviors for functions.
Are you talking about dynamic scope or * Dynamic lookup*?
– Daniel Falbel
Dynamic scope.
– neves