9

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?

antonio
  • 10,629
  • 13
  • 68
  • 136
ytsaig
  • 3,267
  • 3
  • 23
  • 27
  • 1
    You should make your edit a new question, since it's very different than your original one. – Josh O'Brien Jul 18 '13 at 20:13
  • Thanks for the suggestion, I did, here it is: [R - evaluate nested function call in a deserialized environment](http://stackoverflow.com/questions/17733323/r-evaluate-nested-function-call-in-a-deserialized-environment) – ytsaig Jul 18 '13 at 20:34

2 Answers2

8

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
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
6

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
Joshua Ulrich
  • 173,410
  • 32
  • 338
  • 418
  • +1 for `inherits=TRUE`. I have to confess, though, I don't see how the variable `"f1"` is found in `jobenv` before it is first assigned there. From the quoted passage, I'd expect the symbol, not being encountered there, to be assigned into the user's workspace. – Josh O'Brien Jul 18 '13 at 19:43
  • Looks like I was right to be skeptical: your first example just assigns `f1`and `f2` into the global environment. (Try `ls()` and `ls(jobenv)` after running it.) And your second example doesn't work either. – Josh O'Brien Jul 18 '13 at 19:49
  • Try (as one solution) `eval(quote(f1 <- function(x) x*2), envir=jobenv)` and `eval(quote(f2 <- function(y) f1(y) + 1), envir=jobenv)`. The key is that the assignments (whether executed by `eval` or `do.call` or whatever) need to be, themselves, evaluated within `jobenv`. Or, I guess, use any of the approaches in this post: http://stackoverflow.com/questions/12982528/how-to-create-an-r-function-programmatically – Josh O'Brien Jul 18 '13 at 19:59
  • Thanks for the detailed answer and the comments. I edited my question to clarify the intended result. – ytsaig Jul 18 '13 at 20:11