Tidy Evaluation and Formulae

What you've shown has nothing to do with formulas, that's just R parsing a string. We don't recommend meta-programming with strings as it's not robust, and not powerful. It's not a tidyverse thing, experimented base R programmers are likely to give the same advice.

Here is an approach to reducing a list of names or expressions to a single expression. First we'll need to define some tools, hopefully they'll live in a package sometime in the future:

syms_reduce <- function(syms, op = "+") {
  exprs_reduce(syms(syms), op = op)
}
exprs_reduce <- function(exprs, op = "+") {
  n <- length(exprs)

  if (length(exprs) == 0) {
    abort("Empty list of expressions")
  }
  if (length(exprs) == 1) {
    return(exprs[[1]])
  }

  op <- sym(op)
  purrr::reduce(exprs, function(x, y) expr((!!op)(!!x, !!y)))
}

We have one function syms_reduce() that takes symbols or strings as inputs, and the other exprs_reduce() takes a list of arbitrary expressions. Let's try syms_reduce() to create a sum, the default operation:

syms_reduce(c("a"))
#> a

syms_reduce(c("a", "b", "c"))
#> a + b + c

This function is robust to any weird names that might come up in messy data:

syms_reduce(c("1()", "`a`e", "_foo"))
#> `1()` + `\`a\`e` + `_foo`

You can specify a different operation:

syms_reduce(c("a", "b", "c"), op = "*")
#> a * b * c

And it even works with regular functions!

syms_reduce(c("a", "b", "c"), op = "plus")
#> plus(plus(a, b), c)

As for exprs_reduce(), it accepts any expression as input, even other sums:

exprs <- exprs(foo(), bar(baz), quux + blip)
exprs_reduce(exprs, "-")
#> foo() - bar(baz) - (quux + blip)

Note how the operator precedence of the last input quux + blip was handled gracefully.

String meta-programming simply doesn't have this kind of flexibility and robustness.

5 Likes