I am confused about the envir and enclos arguments of eval().
enclos: ...
Specifies the enclosure, i.e., where R looks for objects not
found in ‘envir’. ...
Is the enclosure argument just closure defined below?
https://en.wikipedia.org/wiki/Closure_(computer_programming)
If enclos is used when things are not found in envir, why not just combine whatever in enclos into envir as a single argument envir?
CodePudding user response:
Is the enclosure argument just closure defined below?
No, they’re distinct concepts, this has nothing to do with closures.
Environments have parent environments. If a name isn’t found in a given environment, it’s looked up in its parent environment, and so on, until the last environment without a parent is reached. This happens everywhere in R code when a name is looked up. That’s why the following works:
x = 1
f = function (y) x y
f(2) # = 3
Executing f
creates an environment (a “stack frame”) the parent of which is is the global environment. Looking up x
inside f
will find x
in the parent environment.
When you pass an environment to eval
’s envir
argument, the expression is evaluated in that environment, and the same happens:
x = 1
# `environment()` = the *current* environment.
e = new.env(parent = environment())
e$y = 2
eval(quote(x y), envir = e) # = 3
However, eval
also supports passing non-environment arguments in eval
. E.g. lists (or data.frame
s). But lists don’t have the concept of a “parent environment”, so the following wouldn’t work:
x = 1
lst = list(y = 2)
eval(quote(x y), envir = lst)
… because x
isn’t found inside lst
. But in fact this does work; and the reason is enclos
: if you pass something other than an environment to eval
, the enclos
arguments acts in stead of a parent environment of envir
(if you pass an environment in envir
, enclos
is unneeded and will be ignored). In the above I didn’t need to specify enclos
since the argument defaults to the calling environment. I could also set it to something else entirely:
x = 1 # This variable is *not used* below!
e = new.env()
e$x = 3
lst = list(y = 2)
eval(quote(x y), envir = lst, enclos = e) # = 5
CodePudding user response:
The enclos
argument cannot take a data frame or pairlist as an argument. It tells eval
where to look for variables not found in the specified envir
. The following reprex hopefully demonstrates the situations in which it makes a difference:
df <- data.frame(a = 1)
b <- 2
get_b_local <- function() {
b <- 3
eval(quote(c(a, b)), envir = df)
}
get_b_calling_frame <- function() {
b <- 3
eval(quote(c(a, b)), envir = df, enclos = parent.frame())
}
get_b_calling_frame()
#> [1] 1 2
get_b_local()
#> [1] 1 3
Created on 2022-04-03 by the reprex package (v2.0.1)