In R, running the expression x <- 1
defines a variable x
in the global environment with the value 1
. Doing the same within a function defines the variable within the function's environment instead.
Using rlang::with_env
, we can also do the same thing with an arbitrary environment:
e <- new.env()
rlang::with_env(e, {
x <- 1
y <- 2
f <- function(x) print(x)
g <- function() f(1)
})
e$x
#> [1] 1
e$g()
#> [1] 1
Created on 2021-10-26 by the reprex package (v2.0.1)
However, I can't figure out how to do the same in a function. That is, a function which receives expressions and then runs them in a blank environment, returning the environment:
set_in_env <- function(expr) {
e <- new.env()
# q <- rlang::enquo(expr)
# z <- quote(expr)
# rlang::with_env(e, substitute(expr))
# rlang::with_env(e, parse(text = substitute(expr)))
# rlang::with_env(e, q)
# rlang::with_env(e, rlang::eval_tidy(q))
# rlang::with_env(e, z)
# rlang::with_env(e, eval(z))
rlang::with_env(e, expr)
rlang::with_env(e, {x <- 1})
return(e)
}
e <- set_in_env({y <- 2})
rlang::env_print(e)
#> <environment: 0000000014678340>
#> parent: <environment: 0000000014678730>
#> bindings:
#> * x: <dbl> <-- ONLY `x` WAS SET, NOT `y`!
That is, the function is given the expression y <- 2
which should be run in a new environment. For demonstration purposes, the function also internally sets x <- 1
in the environment.
No matter what I've tried, the environment is only created with e$x
, never defining e$y <- 2
(the commented out code were other failed attempts).
I'm confident this can be done and that I'm just missing something. So, can someone give me a hand?
I raised this as an issue in the rlang
GitHub, where among other comments (including that he intends to deprecate with_env
) @lionel gave a very clean way of doing this:
library(rlang)
set_in_env <- function(expr) {
e <- env()
expr <- rlang::enexpr(expr)
rlang::eval_bare(expr, e)
return(e)
}
e <- set_in_env({y <- 2})
e$y
#> [1] 2
Created on 2021-10-27 by the reprex package (v2.0.1)
I'd actually tried eval_tidy
with quosures, what I needed was eval_bare
with expressions.