In this question, the following throws an error:
subset2 = function(df, condition) {
condition_call = eval(substitute(condition),df )
df[condition_call,]
}
df = data.frame(a = 1:10, b = 2:11)
condition = 3
subset2(df, a < condition)
## Error in eval(substitute(condition), df) : object 'a' not found
Josh and Jason from the original question did a great job explaining why this is. What I don't get is why supplying the enclos
argument to eval apparently fixes it.
subset3 = function(df, condition) {
condition_call = eval(substitute(condition), envir = df, enclos = parent.frame())
df[condition_call, ]
}
subset3(df, a < condition)
## a b
## 1 1 2
## 2 2 3
I understand that skipping the function environment means R is no longer trying to evaluate the promise, and instead grabs the condition object from the global environment.
But I think supplying enclos = parent.frame()
should not make a difference. From ?eval
on the enclos argument:
Specifies the enclosure, i.e., where R looks for objects not found in envir.
But if not provided, it defaults to:
enclos = if(is.list(envir) || is.pairlist(envir)) parent.frame() else baseenv())
which, in my mind, should resolve to parent.frame()
anyway, because surely, df
satisfies the is.list()
check.
This means that as long as some object data
returns TRUE on is.list()
, the behavior of eval(expr, envir = data)
and eval(expr, envir = data, enclos = parent.frame())
should be identical. But as evidenced by the above, it isn't.
What am I missing?
EDIT: Thanks to SmokeyShakers who pointed out the difference between default and user-supplied arguments regarding the time of evaluation. I think this is actually already expressed here: https://stackoverflow.com/a/15505111/2416535
It might make sense to keep this one alive though, as it touches eval()
specifically (the other does not), and it is not trivial to realize what the generalized question should be until one has the answer.
So, different parents. In the example that doesn't work parent.frame
is looking up from inside eval
into the internal environment of subset2
. In the working example, parent.frame
is looking up from inside subset3
, likely to your global where your df sits.
Example:
tester <- function() {
print(parent.frame())
}
tester() # Global
(function() {
tester()
})() # Anonymous function's internal env