kennyB kennyB - 3 months ago 7
R Question

How do I pass ``...`` to a new environment in R?

My ultimate goal is to be able to have a function that:


  • operates on a function and returns a function

  • The return function creates a new environment with .GlobalEnv as its parent and,

  • evaluates the argument function inside the new environment.



It would work something like this:

# Create an object that doesn't exist in the new.env
iris2 <- iris
model <- in_new_env(lm)(Sepal.Length ~ Sepal.Width, iris2)


The reason I want to do this is that I often have applications where I want to generate a model/ggplot inside a function that contains large items that aren't directly used in the model/ggplot call. Because these objects carry around their calling environment, the objects end up being very large when saved, or moved back from a parallel cluster.

My start attempt is:

in_new_env <- function(.f){
function(...) {
env <- new.env(parent = globalenv())
# This doesn't seem to actually export the ... to env
assign("...", ..., envir = env)
env$.f <- .f
with(env, .f(...))
# Error in eval(expr, envir, enclos) : '...' used in an incorrect context
}
}


This would work if I was able to export the
...
to
env
. How do I do this? Is it possible? Do you suggestions for an alternative strategy for
in_new_env
?

This would then be a convenient wrapper for the solution suggested by Bill Dunlap here.

Answer

The ... really isn't a variable itself so you can't really assign to it. If I think I understand what you are trying to do, you can use something like list(...) to evaluate all the parameters passed to a function and store them in a list. Then you can use do.call() to pass that list of parameters to another function and evaluate that in a different environment with evalq. I think this does what you want...

in_new_env <- function(.f){
  function(...) {
    params <- list(...)
    env <- new.env(parent = globalenv())
    assign(".params.", params, envir = env)
    env$.f <- .f
    evalq(do.call(".f", .params.), envir=env)

  }
}

iris2 <- iris
model <- in_new_env(lm)(Sepal.Length ~ Sepal.Width, iris2)

Of course the call to the object is a bit off in that it remembers the .f function name but it wasn't clear to me what your plan for that was with your proposed function.