Set a Functions Environment to That of the Calling Environment (Parent.Frame) from Within Function

Set a functions environment to that of the calling environment (parent.frame) from within function

Well, a function cannot change it's default environment, but you can use eval to run code in a different environment. I'm not sure this exactly qualifies as elegant, but this should work:

helpFunction<-function(){
eval(quote(importantVar1+1), parent.frame())
}

mainFunction<-function(importantVar1){
return(helpFunction())
}

mainFunction(importantVar1=3)

Is it possible to set a function's environment while runtime in R?

With this code you get exactly what you're looking for:

subfun0 <- function() {
e <- parent.frame()
attr(e, "name") <- "my_env"
assign("my_env", 1,
envir = parent.frame(),
inherits = FALSE, immediate = TRUE)
return(NULL)
}

subsubfun <- function() {
print(" subsubfun")
print(" =========")
print(exists("my_env"))
print(exists("my_env", parent.frame()))
print(exists("my_env", parent.frame(2)))
return(NULL)
}

subfun <- function() {
print(" subfun")
print(" ======")
print(exists("my_env"))
print(exists("my_env", parent.frame()))
print(exists("my_env", parent.frame(2)))
subsubfun()
return(NULL)
}

fun1 <- function() {
print("fun1")
print("====")
subfun0()
print(exists("my_env"))
print(exists("my_env", parent.frame()))
print(exists("my_env", parent.frame(2)))
subfun()
return(NULL)
}

fun1()
[1] "fun1"
[1] "===="
[1] TRUE
[1] FALSE
[1] FALSE
[1] " subfun"
[1] " ======"
[1] FALSE
[1] TRUE
[1] FALSE
[1] " subsubfun"
[1] " ========="
[1] FALSE
[1] FALSE
[1] TRUE
NULL

The point is that: parent.frame(2) is not equal to parent.env(parent.frame())

Is in case of R functions a parent's parent environment of an environment also parent to the environment?

The problem is that you are actually defining all your functions in the global environment, therefore their parent is the global environment.

You get what you would expect if you define your functions inside other functions.
Look at the example below.

(Also I created a function that prints all parent environments till the global environment)

env_genealogy <- function(env){

while(!identical(env, globalenv())){

env <- parent.env(env)
print(env)

}

}

fun1 <- function() {
subfun0 <- function() {

print(" subfun0")
print(" ======")

env_genealogy(environment())

e <- parent.frame()
attr(e, "name") <- "my_env"
assign("my_env", 1,
envir = parent.frame(),
inherits = FALSE, immediate = TRUE)
return(NULL)
}


subfun <- function() {

subsubfun <- function() {
print(" subsubfun")
print(" =========")

env_genealogy(environment())

print(exists("my_env"))
print(exists("my_env", parent.frame()))
env <- parent.frame()
print(exists("my_env", parent.env(env)))
# print(parent.env(env)) # <environment: R_GlobalEnv>??
return(NULL)
}

print(" subfun")
print(" ======")

env_genealogy(environment())

print(exists("my_env"))
print(exists("my_env", parent.frame()))
env <- parent.frame()
print(exists("my_env", parent.env(env)))
subsubfun()
return(NULL)
}



print("fun1")
print("====")

env_genealogy(environment())

subfun0()
print(exists("my_env"))
print(exists("my_env", parent.frame()))
env <- parent.frame()
print(exists("my_env", parent.env(env)))
subfun()
return(NULL)
}

fun1()
[1] "fun1"
[1] "===="
<environment: R_GlobalEnv>
[1] " subfun0"
[1] " ======"
<environment: 0x000001b0e4b124d8>
<environment: R_GlobalEnv>
[1] TRUE
[1] FALSE
[1] FALSE
[1] " subfun"
[1] " ======"
<environment: 0x000001b0e4b124d8>
attr(,"name")
[1] "my_env"
<environment: R_GlobalEnv>
[1] TRUE
[1] TRUE
[1] FALSE
[1] " subsubfun"
[1] " ========="
<environment: 0x000001b0e552add0>
<environment: 0x000001b0e4b124d8>
attr(,"name")
[1] "my_env"
<environment: R_GlobalEnv>
[1] TRUE
[1] TRUE
[1] TRUE
NULL

For more details, have a look here


For a minimal example, you can look at this:

a <- function(){

i

}

a()
> #> Error in a() : object "i" not found

b <- function(){

i <- 1

a()

}
b()
> #> Error in a() : object "i" not found

d <- function(){

i <<- 1
a()

}
d()
#> [1] 1
rm(i)


f <- function(){

g <- a
i <- 2
g()

}
f()
#> Error in g() : object "i" not found

h <- function(){

l <- function() i
i <- 2
l()

}
h()
#> [1] 2

When you call a() you get an error because i was not defined.

Even if you define i inside b() you get the same error because b's environment is not shared with a. This is your case.

d() works because we assign i to the global environment with <<-.

f() doesn't work: even if we defined g inside f, we made a copy of a which copied also its parent environment.

We get a result in h() because l() was defined inside. This is the case I showed you in my answer.

apply() and forceAndCall() ignoring get() from parent.frame()

The key issue here are the calling environments and the way get(inherits=TRUE) handles them. For starters, I suggest one reads Hadley's Function environments.

More specifically, one has to understand function's enclosing and calling environments to understand what is going on.

v is defined in the function f calling environment, which is -- looking from the function's execution environment -- parent.frame(1L) in the first print statement, and parent.frame(2L) in the last two print statements.

I thought that if I supply envir=parent.frame(1L) with inherits=TRUE, the get function would traverse the parent.frame() hierarchy and eventually find v in one of the parent calling environments. It does not. In reality it starts from the envir environment one supplies and looks in the parent enclosing environments for v. For the second print statement, the enclosing parent environment of parent.frame(1L) is exactly where v is defined, but in the last print statement, the enclosing parent environment of parent.frame(1L) is the base namespace where apply is defined. Enclosing parent environment on top of that is the global environment. So v is not found.

What we really need is a get function that doesn't search in the parent enclosing environments hierarchy for v, but in parent calling environments hierarchy. And that is exactly what dynget() does.

dynGet() is somewhat experimental and to be used inside another
function. It looks for an object in the callers, i.e., the
sys.frame()s of the function. Use with caution

This function will traverse the parent calling environments sys.parents() with sys.frame() and look for v in each of them. So f <- function(x) dynGet('v', inherits = TRUE) solves the problem.

Why doesn't R look up for the specified object in the provided envrionment parents tree?

So, this is an unusually nuanced issue. There are two relevant types of environments that you need to think about here, the binding environment, or the environment that has a binding to your function, and the enclosing environment, or the environment where your function was created. In this case the binding environment is e10, but the enclosing environment is the global environment. From Hadley Wickham's Advanced R:

The enclosing environment belongs to the function, and never changes, even if the function is moved to a different environment. The enclosing environment determines how the function finds values; the binding environments determine how we find the function.

Consider the following (executed after executing your supplied code) that demonstrates this:

eval(expression(testfun()), envir = e10)
# [1] "e10"
# Error in testfun() : object 'testvar' not found
testvar <- 600
eval(expression(testfun()), envir = e10)
# [1] "e10"
# [1] 600

Moreover, now consider:

eval(envir = e10, expr = expression(
testfun2 <- function(x) {
print(envnames::environment_name(caller_env()))
return (testvar)
}
))
eval(expression(testfun2()), envir = e10)
# [1] "e10"
# [1] 1200

I hope this clarifies the issue.

Update: Determining the Enclosing and Binding Environments

So how can we determine the binding and enclosing environments for functions such as testfun()?

As G. Grothendieck's answer shows, the environment() function gives you the enclosing environment for a function:

environment(e10$testfun)
# <environment: R_GlobalEnv>

To my knowledge, there isn't a simple function in base R to give you a function's binding environments. If the function you're looking for is in a parent environment, you can use pryr::where():

pryr::where("mean")
# <environment: base>

(There is a base function to see if a function is in an environment, exists(), and pryr::where() uses it. But, it doesn't recurse through parent environments like where().)

However, if you're having to search through child environments, to my knowledge there isn't a function for that. But, seems pretty simple to mock one up:

get_binding_environments <- function(fname) {
## First we need to find all the child environments to search through.
## We don't want to start from the execution environment;
## we probably want to start from the calling environment,
## but you may want to change this to the global environment.
e <- parent.frame()
## We want to get all of the environments we've created
objects <- mget(ls(envir = e), envir = e)
environments <- objects[sapply(objects, is.environment)]
## Then we use exists() to see if the function has a binding in any of them
contains_f <- sapply(environments, function(E) exists(fname, where = E))
return(unique(environments[contains_f]))
}

get_binding_environments("testfun")
# [[1]]
# <environment: 0x55f865406518>

e10
# <environment: 0x55f865406518>


Related Topics



Leave a reply



Submit