Reputation: 216
In learning metaprogramming with R, I'm trying to understand the different concepts and mechanisms that form the tidy evaluation framework. I don't really understand the difference between expr()
and enexpr()
(or quo()
and enquo()
and all their plurals for that matter).
The only thing I understand is that the enriched en
-versions should be used for capturing function arguments. However, experimenting with this is giving me mixed results:
library(rlang)
x <- 1000
a <- 9
b <- 2
f1 <- function(x) {
e <- expr(x)
print(e)
print(eval(e))
}
f2 <- function(x) {
e <- enexpr(x)
print(e)
print(eval(e))
}
Feeding these functions an expression, the output is this:
> f1(a + b)
x
[1] 11
> f2(a + b)
a + b
[1] 11
From that, several things don't make sense to me. In the first function with expr()
, even though the expression is just x
in its quoted/defused form when printed, it evaluates to 11, the value of a + b
. So even if it does not find x
within the function scope, shouldn't it be able to find x <- 1000
in the global environment?
So then I thought, the only explanation is that there would still be a presence of a
and b
in the abstract syntax tree (AST) of the expression e
. Indeed it seems to go out of its way to still find a
and b
in the global environment, choosing them over x
. But why is that?
Upvotes: 1
Views: 64
Reputation: 206232
The differences between expr
and enexpr
has to do with lazy evaluation. R doesn't evaluate the value of parameters passed to function until the value is actually used. Until they are evaluated, they are in a "promise" state. The expr()
function basically takes whatever expression you pass to it and keeps it an expression. enexpr()
however takes a symbol, and grabs the expression contained in the promise that variable points to (assuming it hasn't been evaluated). Something like expr(a+b)
is valid, but enexpr(a+b)
is invalid. You can only pass a symbol to enexpr
Your functions won't find x
in the global environment because you've created a shadow variable also named x
in if your function body. It will contain the value passed to the function. So when you evaluate the symbol x
, you get the value that x
has in your local function, not the global environment. Just like if you call
f <- function(x) {
x
}
f(a+b)
[1] 11
you get the value you would expect. If you change the variable name, you get the different behavior
f3 <- function(z) { # note "z" here, no longer masking global "x"
e <- expr(x)
print(e)
print(eval(e))
}
f3(a+b)
# x
# [1] 1000
Upvotes: 0