17.8 Quosures
To make the problem more obvious, I’m going to modify with2()
. The basic problem still occurs without this modification but it’s much harder to see.
function(df, expr) {
with2 <- 1000
a <-eval_tidy(enexpr(expr), df)
}
We can see the problem when we use with2()
to refer to a variable called a
. We want the value of a
to come from the binding we can see (10), not the binding internal to the function (1000):
data.frame(x = 1:3)
df <- 10
a <-with2(df, x + a)
#> [1] 1001 1002 1003
The problem arises because we need to evaluate the captured expression in the environment where it was written (where a
is 10), not the environment inside of with2()
(where a
is 1000).
Fortunately we can solve this problem by using a new data structure: the quosure which bundles an expression with an environment. eval_tidy()
knows how to work with quosures so all we need to do is switch out enexpr()
for enquo()
:
function(df, expr) {
with2 <- 1000
a <-eval_tidy(enquo(expr), df)
}
with2(df, x + a)
#> [1] 11 12 13
Whenever you use a data mask, you must always use enquo()
instead of enexpr()
. This is the topic of Chapter 20.