Suppose that you have an argument in a R function that could be either:
contains("a")
, starts_with("a")
etc, vars(contains("a"))
. How do you check within the function if you are in case (1) or (2)?
The problem is that is_quosures(vars(contains("a")))
works, but is_quosures(contains("a"))
will not work, as it tries first to evaluate the function contains("a")
, which returns an error when evaluated alone!?
library(rlang)
library(dplyr)
is_quosures(vars(contains("a")))
#> [1] TRUE
is_quosures(contains("a"))
#> Error: No tidyselect variables were registered
fo <- function(var) {
is_quosures(var)
}
fo(vars(contains("a")))
#> [1] TRUE
fo(contains("a"))
#> Error: No tidyselect variables were registered
Created on 2019-12-03 by the reprex package (v0.3.0)
You want to use a function such as summarise_at(data, var)
, and want to make it robust to the user specifying var
as a direct tidyselect
helper, or wrapped within a vars()
call. Only way I figured out to handle this is to do a case-by-case if/then checking if it is a quosure or not (then wrap into vars if not), but this will precisely fail in the case above.
library(rlang)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
fo <- function(var) {
is_var_closure <- rlang::is_quosures(var)
if(is_var_closure) {
dplyr::summarise_at(iris, var, mean)
} else {
dplyr::summarise_at(iris, quos(var), mean)
}
}
fo(vars(starts_with("Sepal")))
#> Sepal.Length Sepal.Width
#> 1 5.843333 3.057333
fo(starts_with("Sepal"))
#> Error: No tidyselect variables were registered
Created on 2019-12-03 by the reprex package (v0.3.0)
The way I've done this before is by capturing the expression and checking with is_call
:
f <- function(var) {
if (rlang::is_call(rlang::enexpr(var), names(tidyselect::vars_select_helpers))) {
rlang::enquos(var)
}
else {
stopifnot(rlang::is_quosures(var)) # or something more specific with a useful message
var
}
}
# both work
f(vars(starts_with("Sepal")))
f(starts_with("Sepal"))
Just make sure to use enexpr
for is_call
,
see this GitHub issue.