The code in the question defines a function in the global environment. We can query its environment like this:
environment(e10$testfun)
## <environment: R_GlobalEnv>
When a function looks up a free variable (one that is referenced but not defined in the function) such as testvar
it uses the function's environment (and ancestors) to find the variable. testvar
is in e4
but e4
is not an ancestor of the global environment so in the question testvar
is not found.
Other environments are irrelevant. If the function is called then the environment of the caller (also known as the parent frame) plays no part at all in the variable lookup. Similarly, if the function is later placed somewhere else (in this case e10
) that environment also plays no part in variable lookup. Furthermore, realize that when the function in the question is placed into e10
it has already been defined in the global environment and so the global environment has already been set as its environment. A function consists of arguments, body and environment (and attributes such as class) and moving the function to be somewhere else does not change any of those constituents.
Fixing
We would need to explicitly set the environment of testfun
to e10
if we wanted it to have that as its environment and so to have e4
as an ancestor:
environment(e10$testfun) <- e10
or, alternately, we could not define testfun
in the global environment in the first place but rather define testfun
in the e10
environment right from the start:
with(e10, {
testfun <- function() testvar
})
e10$testfun()
## [1] 1200
Function names
Another point of confusion may be the misconception that the following statement is defining the function named testfun
in e10
.
e10$testfun <- function() testvar
The problem with that idea is that functions do not have names. The three constituents of a function are arguments, body and environment (and attributes such as class and possibly srcref and scrfile). The name is not a constituent of a function. One can place a function in a variable and refer to that variable as if it were the name of the function but in reality it is just a variable that holds the function and the name is not part of the function itself. Thus in the above line of code we are not defining the function named testfun; rather, we are defining an anonymous function (in the global environment) and then moving it into the variable testfun
.
An Example from R Itself
While it is common for user created functions to remain in the environment in which they are defined, e.g.
f <- function() "hello"
# the environment of f
environment(f)
## <environment: R_GlobalEnv>
# the environment where f is located (same)
as.environment(find("f"))
## <environment: R_GlobalEnv>
functions in R packages on the search path
# show search path
search()
normally are not located in their environment. For any function in a package on the search path the function will have the namespace of the package as its environment but when you access the function it will be found, not in the namespace but in a different environment.
# the environment of function mean
e1 <- environment(mean); e1
## <environment: namespace:base>
# where mean is located
e2 <- as.environment(find("mean")); e2
## <environment: base>
# these are NOT the same
identical(e1, e2)
## [1] FALSE
This is well illustrated in diagrams in this blog post: http://blog.obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/
proto
There is a package that works in the way the question seems to expect. proto objects are like environments but if you assign a function to them then the environment of the function is changed to be the proto object/environment that they are assigned to. (There are other differences too but we focus on this one.)
First we define a proto object p
whose parent is e9 and then assign the function of interest to p
. Finally we run that function. (The first argument is implicitly the proto object so we omit it.) We see that the function has indeed had its environment reset and that e4 is now an ancestor of its environment without explicitly setting it.
library(proto)
p <- proto(e9) # define proto object whose parent is e9
p$testfun <- function(self) testvar
identical(p, with(p, environment(testfun))) # testfun's environment is now p
## [1] TRUE
p$testfun()
## [1] 1200