I would like to ask if it is possible to copy/move all the objects of one environment to another, at once. For example:
f1 <- function() {
print(v1)
The other current solutions who actually attempt to make a copy will all fail if the environment contains promises, because they convert environments to lists.
The solution below works in these cases. Following @geoffrey-poole 's idea I propose an argument to deep copy or not, and I showcase the function on a test case.
It uses the unexported function is_promise2()
from te package {pryr}. I don't know of a base R equivalent.
The function
clone_env <- function(env, deep = FALSE) {
# create new environment with same parent
clone <- new.env(parent = parent.env(env))
for(obj in ls(env, all.names = TRUE)) {
promise_lgl <- pryr:::is_promise2(as.symbol(obj), env = env)
if(promise_lgl) {
# fetch promise expression, we use bquote to feed the right unquoted
# value to substitute
promise_expr <- eval(bquote(substitute(.(as.symbol(obj)), env = env)))
# Assign this expression as a promise (delayed assignment) in our
# cloned environment
eval(bquote(
delayedAssign(obj, .(promise_expr), eval.env = env, assign.env = clone)))
} else {
obj_val <- get(obj, envir = env)
if(is.environment(obj_val) && deep) {
assign(obj, clone_env(obj_val, deep = TRUE),envir= clone)
} else {
assign(obj, obj_val, envir= clone)
}
}
}
attributes(clone) <- attributes(env)
clone
}
Shallow copy
Let's build an environment containing a character variable, a promise (note that a
is undefined), and a nested environment.
create_test_env <- function(x = a){
y <- "original"
nested_env <- new.env()
nested_env$nested_value <- "original"
environment()
}
env <- create_test_env()
ls(env)
#> [1] "nested_env" "x" "y"
# clone it, with deep = FALSE
shallow_clone <- clone_env(env, deep = FALSE)
#> Registered S3 method overwritten by 'pryr':
#> method from
#> print.bytes Rcpp
ls(shallow_clone)
#> [1] "nested_env" "x" "y"
# the promise was copied smoothly
a <- 42
shallow_clone$x
#> [1] 42
# We can change values independently
shallow_clone$y <- "modified"
env$y
#> [1] "original"
# except if we have nested environents!
shallow_clone$nested_env$nested_value <- "modified"
env$nested_env$nested_value
#> [1] "modified"
Deep copy
Let's do it all over again, but with a deep clone now, we see the nested values are distinct this time.
env <- create_test_env()
deep_clone <- clone_env(env, deep = TRUE)
a <- 42
deep_clone$x
#> [1] 42
deep_clone$y <- "modified"
env$y
#> [1] "original"
deep_clone$nested_env$nested_value <- "modified"
env$nested_env$nested_value
#> [1] "original"
Created on 2020-09-10 by the reprex package (v0.3.0)