I'm trying to nest a function that glues together two strings within a function that uses the combined string to name a column of a dataframe. However, the problem seems to be that the glue expression is not evaluated to a string early enough. Can (and should) I force the expression to be evaluated before it is being passed on as an argument to another function?
library(tidyverse)
# define inner function
add_prefix <- function(string) {
  x <- glue::glue("prefix_{string}")
  return(as.character(x))
}
# define outer function
mod_mtcars <- function(df, name) {
  df %>% 
    mutate({{ name }} := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
#> Error: The LHS of `:=` must be a string or a symbol
# alternative outer function with explicit enquoting
mod_mtcars2 <- function(df, name) {
  name <- ensym(name)
  
  df %>% 
    mutate(!!name := mpg ^ 2)
}
mod_mtcars2(mtcars, add_prefix("foo"))
#> Error: Only strings can be converted to symbols
Created on 2021-10-30 by the reprex package (v2.0.1)
First of all, the glue string syntax is now preferred over embracing directly in the LHS. So prefer "{{ var }}" := expr to {{ var }} := expr. In a future version of rlang (next year) we'll make it possible to use glue strings with =. At that point, := will be pretty much superseded. We went with := to allow !! injection on the LHS before glue support was added.
Second, your problem is that you're using {{ instead of simple injection. {{ is for injecting the expression supplied as argument, not the value of the expression. Use normal glue interpolation with "{" to inject the value instead:
mod_mtcars <- function(df, name) {
  df %>% 
    mutate("{name}" := mpg ^ 2)
}
PS: Your !! version had a similar problem. Because you used ensym() on the argument, you were defusing the expression supplied as argument instead of using the value. But ensym() requires the expression to be a simple name and you supplied a full computation, causing an error. You can fix it like this:
mod_mtcars2 <- function(df, name) {
  df %>%
    mutate(!!name := mpg ^ 2)
}
But glue syntax is now preferred.
name is not a symbol. Try:
mod_mtcars <- function(df, name) {
  name <- sym(name)
  df %>%
    mutate({{ name }} := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
Even better, tidy eval now supports glue strings so you could simplify with:
# don't need add_prefix()
mod_mtcars <- function(df, name){
  df %>% 
    mutate("prefix_{{name}}" := mpg ^ 2)
}
mod_mtcars(mtcars, foo)
Lastly, the curly-curly operator tunnels your expression and you're passing it a string. If you keep your current functions use the bang-bang operator instead:
mod_mtcars <- function(df, name){
  df %>% 
    mutate(!! name := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With