Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the mechanism that makes `+` work when defined by + in empty environment?

Tags:

r

Here I create an unevaluated expression:

e2 <- expression(x+10)

If I supply an environment in which x is defined like

env <- as.environment(list(x=20))
eval(e2,env)

R will report an error:

Error in eval(expr, envir, enclos) : could not find function "+"

It is understandable since env is an environment created from scratch, that is, it has no parent environment where + is defined.

However, if I supply + in the list to be converted to an environment like this

env <- as.environment(list(x=20,`+`=function(a,b) {a+b}))
eval(e2,env)

The evaluation works correctly and yields 30.

However, when I define + in the list, it is a binary function whose body also uses + which is defined in {base}. I know that function returns are lazily evaluated in R, but why this could work? If a+b in the function body is lazily evaluated, when I call eval for e2 within env, even though + is defined in this environment which has no parent environment, it should still calls + in itself, which should end up in endless loop. Why is does not happen like this? What is the mechanism here?

like image 575
Kun Ren Avatar asked Apr 02 '14 08:04

Kun Ren


1 Answers

When you define the environment here:

env <- as.environment(list(x=20,`+`=function(a,b) {a+b}))

then the function definition is actually defined in the .GlobalEnv (namely, where the definition is executed. You can verify this:

$ environment(env$`+`)
<environment: R_GlobalEnv>

This observation is worth being pondered a little: a function can be a member of environment x, yet belong to y (where “belong to” means that its object lookup uses y rather than x).

And .GlobalEnv knows about +, since it is defined somewhere in its parents (accessible via search()).

Incidentally, if you had used list2env instead of as.environment, your initial code would have worked:

$ env = list2env(list(x = 20))
$ eval(e2, env)
30

The reason is that, unlike as.environment, list2env by default uses the current environment as the new environment’s parent (this can be controlled via the parent argument). as.environment by contrast uses the empty environment (when creating an environment from a list).

like image 83
Konrad Rudolph Avatar answered Oct 25 '22 23:10

Konrad Rudolph