I am trying to write a custom function where I want to use the cor.test
function but I am having trouble unquoting the needed arguments to create a working formula.
Here is what I currently have that doesn't work-
library(rlang)
# custom function
tryfn <- function(data, x, y) {
stats::cor.test(
formula = rlang::new_formula(NULL, {{ x }} + {{ y }}),
data = data,
method = "pearson"
)
}
# using the function
tryfn(mtcars, wt, mpg)
#> Error in rlang::new_formula(NULL, {: object 'wt' not found
I tried this way because it seems to work if I don't have to unquote the formula in the function environment.
# without unquoting inside another function
print(rlang::new_formula(NULL, quote(x + y)))
#> ~x + y
Any ideas on how to implement this?
It's important to remember that rlang::quo
is not the same as base::quote
.
In practice, the latter ends up being essentially equivalent to rlang::expr
.
Interpolation with {{
creates quosures with their corresponding environments,
so it's a shortcut for a case like the following:
x <- 0
with_curly <- function(foo) {
x <- 1
rlang::eval_tidy({{ foo }})
}
with_curly(x)
# 0
with_enquo <- function(foo) {
x <- 1
rlang::eval_tidy(rlang::enquo(foo))
}
with_enquo(x)
# 0
On the other hand, enexpr
acts like quote
but for what the user typed:
with_enexpr <- function(foo) {
x <- 1
rlang::eval_tidy(rlang::enexpr(foo))
}
with_enexpr(x)
# 1
In my experience, quosures don't play nicely (or at all) with any function that doesn't support them explicitly, and many R functions expect "raw" expressions. Even during printing you can see that they aren't treated the same:
foo <- function(foo) {
rlang::qq_show({{ foo }})
rlang::qq_show(!!rlang::enexpr(foo))
invisible()
}
foo(x)
# ^x
# x
That means, at least for now, there's no shortcut for creation of simple expressions, and you'll have to do it the long way:
EDIT: not entirely true. There's no shortcut for simple expressions, but you can still create formulas with quosures. See Moody's answer and the comments below.
It's also worth taking a step back every now and then and remember that you don't need non-standard evaluation everywhere:
tryfn <- function(data, x, y) {
stats::cor.test(
formula = as.formula(glue::glue("~ {x} + {y}")),
data = data,
method = "pearson"
)
}
tryfn(mtcars, "wt", "mpg")