title: Using quoted variables in a custom dplyr wrapper function
body: <p>My problem is the following. I have a function <code>foo</code> which works inside <code>dplyr::mutate</code>. This function accepts <code>tidyselect</code> syntax. I want to build a wrapper function <code>bar</code> which should also support <code>tidyselect</code> syntax. I am looking for a clean way to pass the <code>tidyselect</code>ed columns from <code>bar</code> to <code>foo</code>. Sounds easy, but the problem is that <code>foo</code> needs to accept bare user input which will be quoted and it also need to accept already quoted columns which come from the wrapper function.</p>
  <p>So lets have a look at the problem:</p>
  
  <pre class="lang-r prettyprint-override"><code>library(dplyr)
  
  myiris &lt;- as_tibble(iris)
  
  # this is a minimal function supporting tidyselect
  # its a toy function, which just returns the tidyselected columns 
  
  foo &lt;- function(cols){
    data &lt;- cur_data()
    vars &lt;- tidyselect::eval_select(rlang::enquo(cols),  data)
    out &lt;- data[, vars]
    
    names(out) &lt;- paste0(&quot;new_&quot;, names(out))
    out
  }
  
  # the function is working:
  myiris %&gt;%
    mutate(foo(c(Sepal.Length)))
  #&gt; # A tibble: 150 x 6
  #&gt;    Sepal.Length Sepal.Width Petal.Length Petal.Width Species new_Sepal.Length
  #&gt;           &lt;dbl&gt;       &lt;dbl&gt;        &lt;dbl&gt;       &lt;dbl&gt; &lt;fct&gt;              &lt;dbl&gt;
  #&gt;  1          5.1         3.5          1.4         0.2 setosa               5.1
  #&gt;  2          4.9         3            1.4         0.2 setosa               4.9
  #&gt;  3          4.7         3.2          1.3         0.2 setosa               4.7
  #&gt;  4          4.6         3.1          1.5         0.2 setosa               4.6
  #&gt;  5          5           3.6          1.4         0.2 setosa               5  
  #&gt;  6          5.4         3.9          1.7         0.4 setosa               5.4
  #&gt;  7          4.6         3.4          1.4         0.3 setosa               4.6
  #&gt;  8          5           3.4          1.5         0.2 setosa               5  
  #&gt;  9          4.4         2.9          1.4         0.2 setosa               4.4
  #&gt; 10          4.9         3.1          1.5         0.1 setosa               4.9
  #&gt; # … with 140 more rows
  
  # this is a wrapper function around `foo`
  bar &lt;- function(df, .cols) {
    .cols &lt;- rlang::enquo(.cols)
    mutate(df, foo(.cols))
  }
  
  # this will throw an error
  myiris %&gt;%
    bar(Sepal.Length)
  
  #&gt; Note: Using an external vector in selections is ambiguous.
  #&gt; ℹ Use `all_of(.cols)` instead of `.cols` to silence this message.
  #&gt; ℹ See &lt;https://tidyselect.r-lib.org/reference/faq-external-vector.html&gt;.
  #&gt; This message is displayed once per session.
  #&gt; Error: Problem with `mutate()` input `..1`.
  #&gt; x Must subset columns with a valid subscript vector.
  #&gt; x Subscript has the wrong type `quosure/formula`.
  #&gt; ℹ It must be numeric or character.
  #&gt; ℹ Input `..1` is `foo(.cols)`.
  </code></pre>
  <p><sup>Created on 2021-04-14 by the <a href="https://reprex.tidyverse.org" rel="nofollow noreferrer">reprex package</a> (v0.3.0)</sup></p>
  <p>It makes total sense that the above doesn't work. Not obvious to me is how to handle this problem in a clean and consistent way.</p>
  <p>Below I show what I have tried and what kind of mediocre workaround I came up with.</p>
  <p>What I thought I could do is: to check if the columns are already quoted, and if not <code>enquote</code> them. However, this does not seem to be possible. Once the unquoted columns are used for any kind of operation they will be evaluated and change. The <code>enquo</code> has to happen as first thing. But if it happens first, I can't check if they have been quoted already.</p>
  
  <pre class="lang-r prettyprint-override"><code># we would need to check in foo
  # if cols is already quoted or not
  # but this seems not to be possible
  # since `cols` changes, once it is used / touched
  
  foo &lt;- function(cols){
    data &lt;- cur_data()
    if (!rlang::is_quosure(cols)) {
      cols &lt;- enquo(cols)
    }
    vars &lt;- tidyselect::eval_select(cols, data)
    out &lt;- data[, vars]
    
    names(out) &lt;- paste0(&quot;new_&quot;, names(out))
    out
  }
  
  # not working
  iris %&gt;%
    mutate(foo(c(Sepal.Length)))
  #&gt; Error: Problem with `mutate()` input `..1`.
  #&gt; x Must subset columns with a valid subscript vector.
  #&gt; x Can't convert from &lt;double&gt; to &lt;integer&gt; due to loss of precision.
  #&gt; ℹ Input `..1` is `foo(c(Sepal.Length))`.
  </code></pre>
  <p><sup>Created on 2021-04-14 by the <a href="https://reprex.tidyverse.org" rel="nofollow noreferrer">reprex package</a> (v0.3.0)</sup></p>
  <p>At the moment I am using a workaround that I don't like very much. I use the ellipsis <code>...</code> in <code>foo</code> so that I can call it with an additional argument which does not need to be documented. Now <code>foo</code> can be called with a <code>flag</code> argument and in that case <code>foo</code> knows that the columns don't have to be quoted.</p>
  <p>However, I don't think this is a clean solution. I would prefer some kind of function which quotes if not already quoted, or a function which restores the environment of the columns names when they are passed to <code>bar</code>.</p>
  <p>One other possible solution would be to first evaluate the columns in <code>bar</code> and then paste the column name as strings to <code>foo</code>. I haven't tried that, it should work since tidyselect accepts strings, however I would like to avoid evaluating the column names in <code>bar</code> as it doesn't seem very performant.</p>
  <p>Any other ideas welcome.</p>
  
  <pre class="lang-r prettyprint-override"><code># workaround using `...`
  foo &lt;- function(cols, ...){
    
    dots &lt;- rlang::list2(...)
    if (is.null(dots$flag)) {
      cols &lt;- enquo(cols)
    }
    
    data &lt;- cur_data()
    vars &lt;- tidyselect::eval_select(cols, data)
    out &lt;- data[, vars]
    
    names(out) &lt;- paste0(&quot;new_&quot;, names(out))
    out
  }
  
  bar &lt;- function(df, .cols) {
    .cols &lt;- rlang::enquo(.cols)
    mutate(df, foo(.cols, flag = TRUE))
  }
  
  
  # working
  myiris %&gt;%
    mutate(foo(c(Sepal.Length)))
  
  #&gt; # A tibble: 150 x 6
  #&gt;    Sepal.Length Sepal.Width Petal.Length Petal.Width Species new_Sepal.Length
  #&gt;           &lt;dbl&gt;       &lt;dbl&gt;        &lt;dbl&gt;       &lt;dbl&gt; &lt;fct&gt;              &lt;dbl&gt;
  #&gt;  1          5.1         3.5          1.4         0.2 setosa               5.1
  #&gt;  2          4.9         3            1.4         0.2 setosa               4.9
  #&gt;  3          4.7         3.2          1.3         0.2 setosa               4.7
  #&gt;  4          4.6         3.1          1.5         0.2 setosa               4.6
  #&gt;  5          5           3.6          1.4         0.2 setosa               5  
  #&gt;  6          5.4         3.9          1.7         0.4 setosa               5.4
  #&gt;  7          4.6         3.4          1.4         0.3 setosa               4.6
  #&gt;  8          5           3.4          1.5         0.2 setosa               5  
  #&gt;  9          4.4         2.9          1.4         0.2 setosa               4.4
  #&gt; 10          4.9         3.1          1.5         0.1 setosa               4.9
  #&gt; # … with 140 more rows
  
  # working
  myiris %&gt;%
    bar(Sepal.Length)
  
  #&gt; # A tibble: 150 x 6
  #&gt;    Sepal.Length Sepal.Width Petal.Length Petal.Width Species new_Sepal.Length
  #&gt;           &lt;dbl&gt;       &lt;dbl&gt;        &lt;dbl&gt;       &lt;dbl&gt; &lt;fct&gt;              &lt;dbl&gt;
  #&gt;  1          5.1         3.5          1.4         0.2 setosa               5.1
  #&gt;  2          4.9         3            1.4         0.2 setosa               4.9
  #&gt;  3          4.7         3.2          1.3         0.2 setosa               4.7
  #&gt;  4          4.6         3.1          1.5         0.2 setosa               4.6
  #&gt;  5          5           3.6          1.4         0.2 setosa               5  
  #&gt;  6          5.4         3.9          1.7         0.4 setosa               5.4
  #&gt;  7          4.6         3.4          1.4         0.3 setosa               4.6
  #&gt;  8          5           3.4          1.5         0.2 setosa               5  
  #&gt;  9          4.4         2.9          1.4         0.2 setosa               4.4
  #&gt; 10          4.9         3.1          1.5         0.1 setosa               4.9
  #&gt; # … with 140 more rows
  </code></pre>
  <p><sup>Created on 2021-04-14 by the <a href="https://reprex.tidyverse.org" rel="nofollow noreferrer">reprex package</a> (v0.3.0)</sup></p>