0

I'm looking for a way to quote an argument passed to function (consider substitute(), ggplot2's aes(), subset() or data.table's [ behavior), but do it conditionally - only if not already quoted with quote().

This is because I'd like to easily chain functions, that do not make assumptions whether an argument is previously quoted, or not - but instead substitute it as needed.

Few questions:

  1. Why is this a bad idea?
  2. Are there already any built-in methods to achieve this goal?
  3. The tryCatch part is because is.language throws an error if an x promise evaluates to a non-defined variable. Is there any other/better solution?

What I've come up with:

substitute_c <- function(x) {
  e <- suppressWarnings(
    tryCatch({x; FALSE}, 
             error = function(e) TRUE))

  if (e || !is.language(x)) {
    substitute_q(substitute(x), env = parent.frame())
  } else {
    x
  }
}

How it works:

require(data.table)
require(dplyr)

# Example 1
fn <- function(x) substitute_c(x)
fn2 <- function(y) fn(substitute_c(y))
fn3 <- function(z) fn2(substitute_c(z))

fn(quote(a + b)
fn(a + b)
fn2(a + b)
fn3(quote(a + b))

# Example 2
dtcars <- mtcars %>% as.data.table

subset_test <- function(dt, cols) {
  cols <- substitute_c(cols)
  dt[, eval(cols)]
}

subset_test2 <- function(dt, cols) {
  cols <- substitute_c(cols)
  subset_test(dt, cols)
}

subset_test(dtcars, .(mpg, cyl))
subset_test(dtcars, list(mpg, cyl))
subset_test2(dtcars, .(mpg, cyl))
subset_test2(dtcars, list(mpg, cyl))
mjktfw
  • 840
  • 6
  • 14
  • 1
    There's not going to be a good way for R to guess if you want your parameters evaluated or not. Especially when nesting functions calls. `quote()` isn't really magic, it's just a function that returns an un-evaluated expression when evaluated. That expression doesn't retain any knowledge of how it was created. But all function parameters are really un-evaluated expressions until you evaluate them. Don't make it difficult for readers of your code to guess what's going on; be explicit. – MrFlick Apr 10 '17 at 20:44
  • 2
    To answer (1): because it doesn’t compose: what if you *need* to quote a quoted argument again? In other words, how would R know whether or not to quote *that specific argument*? That said, R has some corner cases that already exhibit this bad behaviour (for instance `try`: there’s no way of knowing whether it caught an error or whether its expression returned a `simpleError` object). – Konrad Rudolph Apr 10 '17 at 21:04
  • I agree, that it would be troublesome for others, but for personal use it saves me the need of injecting `quote()` anytime I'm calling a function, which is further passing arguments to data.table or ggplot. Please note that all I need is a way for a non-standard evaluation of an argument, that is simply firstly transformed to quoted expression, and later retained as is - no matter which function I begin with, and which will follow. – mjktfw Apr 10 '17 at 21:13
  • @KonradRudolph - for multiple quoting I still can use `enquote()` in relevant function, or provide such transformed argument while calling. – mjktfw Apr 10 '17 at 21:23

0 Answers0