I want to understand why the following happens when evaluating <-
assignment operations using rlang::eval_tidy
.
Let's use rlang::expr
to create an expression to assign the variable x
a value 1.
x_expr <- rlang::expr(x <- 1)
We also create an environment in which we want to evaluate the expression.
new_env <- new.env(parent = parent.env(environment()))
My intention is that we assign 1
to x
inside new_env
.
Now, we use base::eval
to evaluate this expression:
eval(x_expr, envir = new_env)
get('x', envir = new_env)
This returns 1
. If we simply evaluate x
in the global environment, we get an error (indicating x
is in new_env
and not taken from the global environment).
We can remove x
from new_env
, and then re-assign it as we'd like using rlang::eval_bare
:
rm('x', envir = new_env)
rlang::eval_bare(x_expr, env = new_env)
get('x', envir = new_env)
Again, this gives 1
.
Now, we remove x
from new_env
again, and try to re-assign it using rlang::eval_tidy
. However, in this case we get an error when we look for x
in new_env
:
rm('x', envir = new_env)
rlang::eval_tidy(x_expr, env = new_env)
get('x', envir = new_env)
Why does this happen? I presume x
has been assigned 1
, but where?
sessionInfo()
:
R version 4.1.0 (2021-05-18)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)
Matrix products: default
locale:
[1] LC_COLLATE=English_South Africa.1252 LC_CTYPE=English_South Africa.1252
[3] LC_MONETARY=English_South Africa.1252 LC_NUMERIC=C
[5] LC_TIME=English_South Africa.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] compiler_4.1.0 tools_4.1.0 yaml_2.2.1 rlang_0.4.11
From the documentation on eval_tidy()
:
eval_tidy()
always evaluates in a data mask, even when data isNULL
. Because of this, it has different stack semantics thanbase::eval()
:
- Lexical side effects, such as assignment with
<-
, occur in the mask rather thanenv
.
We can confirm this by adding a data mask during tidy_eval()
library(rlang)
x_expr <- rlang::expr(x <- 1)
new_env <- new.env(parent = parent.env(environment()))
data_mask <- rlang::new_data_mask(new.env(parent = parent.env(environment())))
rlang::eval_tidy(x_expr, data_mask, env = new_env)
get('x', envir = data_mask) ## Returns 1