17.3 Code can generate code

  • rlang::call2() creates function call
call2("f", 1, 2, 3)
#> f(1, 2, 3)
  • Going backwards from the tree, can use functions to create calls
call2("f1", call2("f2", "a", "b"), call2("f3", 1))
#> f1(f2("a", "b"), f3(1))
call2("+", 1, call2("*", 2, 3))
#> 1 + 2 * 3
  • !! bang-bang - unquote operator
    • inserts previously defined expressions into the current one
xx <- expr(x + x)
yy <- expr(y + y)
expr(xx / yy)     # Nope!
#> xx/yy

expr(!!xx / !!yy) # Yup!
#> (x + x)/(y + y)
cv <- function(var) {
  var <- enexpr(var)            # Get user's expression
  expr(sd(!!var) / mean(!!var)) # Insert user's expression
}

cv(x)
#> sd(x)/mean(x)
cv(x + y)
#> sd(x + y)/mean(x + y)
  • Avoid paste() for building code
    • Problems with non-syntactic names and precedence among expressions

“You might think this is an esoteric concern, but not worrying about it when generating SQL code in web applications led to SQL injection attacks that have collectively cost billions of dollars.”