I am trying to run a chunk of R code in a sandbox-ed fashion, by loading all the necessary dependencies (functions and data) into a new environment and evaluating an expression within that environment. However, I'm running into trouble with functions calling other functions in the environment. Here's a simple example:
jobenv <- new.env(parent=globalenv())
assign("f1", function(x) x*2, envir=jobenv)
assign("f2", function(y) f1(y) + 1, envir=jobenv)
expr <- quote(f2(3))
Using eval
on expr
fails since f2
can't find f1
> eval(expr, envir=jobenv)
Error in f2(3) : could not find function "f1"
whereas explicitly attaching the environment works
> attach(jobenv)
> eval(expr)
[1] 7
I'm probably missing something obvious, but I couldn't find any permutation of the eval
call that works. Is there a way to get the same effect without attaching the environment?
There are a number of ways of doing this, but I kind of like this one:
jobenv <- new.env(parent=globalenv())
local({
f1 <- function(x) x*2
f2 <- function(y) f1(y) + 1
}, envir=jobenv)
## Check that it works
ls(jobenv)
# [1] "f1" "f2"
local(f2(3), envir=jobenv)
# [1] 7
eval(quote(f2(3)), envir=jobenv)
# [1] 7
Scope is defined when the function is created, not when it's called. See section 10.7 of the Introduction to R manual.
This seems a bit odd to me, but you get the same behavior even if you avoid assign
all together and just use $<-
.
jobenv <- new.env(parent=globalenv())
jobenv$f1 <- function(x) x*2
jobenv$f2 <- function(y) f1(y) + 1
expr <- quote(f2(3))
eval(expr, envir=jobenv)
This seems to be because the enclosing environment of f1
and f2
is the global environment. I would have expected it to be jobenv
.
> environment(jobenv$f1)
<environment: R_GlobalEnv>
> environment(jobenv$f2)
<environment: R_GlobalEnv>
One solution is to explicitly set the environment of each function... but there has to be an easier way.
> environment(jobenv$f1) <- jobenv
> environment(jobenv$f2) <- jobenv
> eval(expr, envir=jobenv)
[1] 7
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With