I suspect that the answer is "No, you have to make a standard evaluation escape hatch", but maybe someone has some ideas.
Imagine writing a function that use non-standard evaluation to get the symbol/name of an object passed into it:
inner_func <- function(inner_arg) {
substitute(inner_arg)
}
inner_func(iris)
#> iris
This falls apart if you call inner_func()
inside a wrapping function, of course.
wrapper_func <- function(wrapper_arg) {
inner_func(wrapper_arg)
}
wrapper_func(iris)
#> wrapper_arg
Is there a way to get the original symbol out of inner_func()
even when it is passed inside another function, without changing the behaviour of inner_func()
? I know that this is possible:
inner_func <- function(inner_arg) {
evalq(as.symbol(inner_arg))
}
wrapper_func <- function(wrapper_func) {
# Get the symbol as a Character
obj_name <- deparse(substitute(wrapper_func))
# eval(as.symbol()) inside inner_func() to turn it back into a symbol
inner_func(obj_name)
}
wrapper_func(iris)
#> iris
But it involves doing some extra processing in inner_func()
that compromises its ability to be called as it was originally:
inner_func(iris)
#> Error in as.vector(x, mode = mode) :
'list' object cannot be coerced to type 'symbol'
If possible, I would prefer to do all of the extra processing in wrapper_func()
.
bquote
approach:inner_func <- function(inner_arg) {
substitute(inner_arg)
}
wrapper_func <- function(wrapper_arg) {
cap_expr <- substitute(wrapper_arg)
eval(bquote(inner_func(.(cap_expr))))
}
wrapper_func2 <- function(wrapper_arg2) {
cap_expr <- substitute(wrapper_arg2)
eval(bquote(wrapper_func(.(cap_expr))))
}
inner_func(iris) # iris
wrapper_func(iris) # iris
wrapper_func2(iris) # iris
substitute
approach:wrapper_func <- function(wrapper_arg) {
eval(substitute(inner_func(wrapper_arg)))
}
wrapper_func2 <- function(wrapper_arg2) {
eval(substitute(wrapper_func(wrapper_arg2)))
}
Let's do a step by step of what happen when you call wrapper_func2(iris)
. I will rewrite wrapper_func2
and wrapper_func
using pipes for ease of explanation:
wrapper_func2 <- function(wrapper_arg2) {
# eval(substitute(wrapper_func(wrapper_arg2))) # original
wrapper_func(wrapper_arg2) |>
substitute() |> # after substitute(wrapper_func(wrapper_arg2)) you get wrapper_func(iris)
# deparse() # toggle deparse() comment here to see
eval() # then evaluate wrapper_func(iris)
}
Next, evaluate wrapper_func(iris)
:
wrapper_func <- function(wrapper_arg) {
# eval(substitute(inner_func(wrapper_arg))) # original
inner_func(wrapper_arg) |>
substitute() |> # after substitute(inner_func(wrapper_arg)) you get inner_func(iris)
# deparse()
eval() # then evaluate inner_func(iris)
}
Next, evaluate inner_func(iris)
inner_func <- function(inner_arg) {
substitute(inner_arg) # after substitute(inner_arg) you get iris
}