localSometimes it’s useful to be able to create a new scope without embedding inside a function. The local() function allows you to do exactly that. For example, to make an operation easier to understand, you can make temporary variables. In this example, df() is created in the global environment, but x and y are not:
df <- local({
x <- 1:10
y <- runif(10)
data.frame(x = x, y = y)
})
This is equivalent to:
df <- (function() {
x <- 1:10
y <- runif(10)
data.frame(x = x, y = y)
})()
(If you’re familiar with JavaScript you’ve probably seen this pattern before. It’s the immediately invoked function expression (IIFE). It’s used extensively by many JavaScript libraries to avoid polluting the global namespace.)
Another useful application of these ideas is local(). In the process of performing a data analysis, you may create variables that are necessary because they help break a complicated sequence of steps down in to easily digestible chunks, but are not needed afterwards. For example, in the following example, we might only want to keep the value of x:
a <- 10
b <- 30
x <- a + b
rm(a)
rm(b)
It’s useful to be able to store only the final result, preventing the intermediate results from cluttering your workspace. One way to do that is to use a function:
x <- (function() {
a <- 10
b <- 30
a + b
})()
x
## [1] 40
a
## Error in eval(expr, envir, enclos): object 'a' not found
(In JavaScript this is called the immediately invoked function expression (IIFE), and is used extensively in modern JavaScript to encapsulate different JavaScript libraries)
R provides a more concise tool to achieve this effect: local().
x <- local({
a <- 10
b <- 30
a + b
})
x
## [1] 40
a
## Error in eval(expr, envir, enclos): object 'a' not found
The idea of local is to create a new environment (inheriting from the current environment) and run the code in that. The essence of local() is captured in this code:
local2 <- function(expr) {
envir <- new.env(parent = parent.frame())
eval(substitute(expr), envir)
}
a <- 100
local2({
b <- a + sample(10, 1)
my_get <<- function() b
})
my_get()
## [1] 102
The real local() code is considerably more complicated because it adds a parameter to specify the environment. I don’t think this is necessary because if you have an explicit environment parameter, then you can already evaluate code in that environment with evalq().
local() is hard to understand because it is very concise and uses some sutble features of evaluation (including non-standard evaluation of both arguments). Confirm that the following function works the same as local() and then explain how it works.
local3 <- function(expr, envir = new.env()) {
call <- substitute(eval(quote(expr), envir))
env <- parent.frame()
eval(call, env)
}