equivalent of within(), attach() etc. for working within an environment?

Viewed 66

I want to add an environment to a search path and modify the values of variables within that environment, in a limited chunk of code, without having to specify the name of the environment every time I refer to a variable: for example, given the environment

ee <- list2env(list(x=1,y=2))

Now I would like to do stuff like

ee$x <- ee$x+1
ee$y <- ee$y*2
ee$z <- 6

but without appending ee$ to everything (or using assign("x", ee$x+1, ee) ... etc.): something like

in_environment(ee, {
    x <- x+1
    y <- y+2
    z <- 6
})

Most of the solutions I can think of are explicitly designed not to modify the environment, e.g.

  • ?attach: "The database is not actually attached. Rather, a new environment is created on the search path ..."
  • within(): takes lists or data frames (not environments) "... and makes the corresponding modifications to a copy of ‘data’"
  • There are two problems with <<-: (1) using it will cause NOTEs in CRAN checks (I think? can't find direct evidence of this, but e.g. see here — maybe this only happens because of the appearance of assigning to a locally undefined symbol? I guess I could put this in a package and test it with --as-cran to confirm ...); (2) it will try to assign in the parent environment, which in a package context [which this is] will be locked ...

I suppose I could use a closure as described in section 10.7 of the Introduction to R by doing

clfun <- function() {
   x <- 1
   y <- 2
   function(...) {
      x <<- x + 1   
      y <<- y * 2
   }
}
myfun <- clfun()

This seems convoluted (but I guess not too bad?) but:

  • will still incur problem #1 (CRAN check?).
  • I think (??) it won't work with variables that don't already exist in the environment (would need an explicit assign() for that ...)
  • doesn't allow a choice of which environment to operate in - it's necessarily going to work in the enclosing environment, not with arbitrary environment ee

Am I missing something obvious and idiomatic?

2 Answers

Thanks to @Nuclear03020704 ! I think with() was what I wanted all along; I was incorrectly assuming that it would also create a local copy of the environment, but it only does this if the data argument is not already an environment.

ee <- list2env(list(x=1,y=2))
with(ee, {
    x <- x+1
    y <- y+2
    z <- 6
})

does exactly that I want.


Just had another idea, which also seems to have some drawbacks: using a big eval clause. Rather than make my question a long laundry list of unsatisfactory solutions, I'll add it here.

myfun <- function() {
    eval(quote( {
        x <- x+1
        y <- y*2
        z <- 3
    }), envir=ee)
}

This does seem to work, but also seems very weird/mysterious! I hate to think about explaining it to someone who's being using R for less than 10 years ... I suppose I could write an in_environment() based on this, but I'd have to be very careful to capture the expression properly without evaluating it ...

What about with()? From here,

with(data, expr)

data is the data to use for constructing an environment. For the default with method this may be an environment, a list, a data frame, or an integer.

expr is the expression to evaluate.

with is a generic function that evaluates expr in a local environment constructed from data. The environment has the caller's environment as its parent. This is useful for simplifying calls to modeling functions. (Note: if data is already an environment then this is used with its existing parent.)

Note that assignments within expr take place in the constructed environment and not in the user's workspace.

with() returns value of the evaluated expr.


ee <- list2env(list(x=1,y=2))
with(ee, {
    x <- x+1
    y <- y+2
    z <- 6
})
Related