Suppose that you have an argument in a R function that could be either:
- a raw tidyselect helper such as
contains("a"),starts_with("a")etc, - a list of quosures with the helpers, with
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)
Use case
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:Just make sure to use
enexprforis_call, see this GitHub issue.