Search code examples
rquosure

R & quosures - How to get names of symbols contained in a vector passed as function argument?


I want to write an R function arg2str that returns the names (that is a vector of strings) of the symbols that are fed as arguments.

For the most simple case, I only have one input symbol:

library ("rlang")

arg2str.v0 <- function (arg) rlang::quo_name (enquo (arg))
arg2str.v0 (a)
## [1] "a"

If I have multiple symbols, I can use the three-dots construct:

arg2str.v1 <- function (...) sapply (enquos (...), rlang::quo_name)
arg2str.v1 (a, b, c)
##             
## "a" "b" "c"

(Subsidiary question: why is the resulting vector of strings displayed with a preliminary line break instead of a preliminary [1] in this case?)

But I actually want to deal with vectors of symbols. Yet:

sym2str.v1 (c(a, b, c))
##
## "c(a, b, c)"

How do I tune my function to do so?


My first intuition was to first enquote the symbols contained in the argument vector by using sapply (instead of enquoting the argument vector itself), then to apply rlang::quo_name to the resulting vector of quosures. But it seems that the symbols in the argument vector are evaluated within sapply before enquote is called on each of them:

arg2str.v2 <- function (args) {
    enquo_args <- sapply (args, enquo)
    lapply (enquo_args, rlang::quo_name)
}
arg2str.v2 (c (a, b, c))
## Error in lapply(X = X, FUN = FUN, ...) : object 'a' not found

Solution

  • One possible hacky answer using substitute and deparse:

    arg2str.v3 <- function (args) {
        strs <- sapply (substitute (args), deparse)
        if (length (strs) > 1) { strs <- strs [-1] }
        return (strs)
    }
    arg2str.v3 (c (a, b, c))
    ## [1] "a" "b" "c"
    

    Note that it also preserves names if a named vector is provided as input:

    arg2str.v3 (c (n1 = a, n2 = b, n3 = c))
    ##  n1  n2  n3 
    ## "a" "b" "c"