Home > Software design >  environment vs enclosure in R
environment vs enclosure in R

Time:04-04

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.frames). 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)

  •  Tags:  
  • r
  • Related