Search code examples
rquotetidyeval

Using quotation to programmatically create a S3 generic


I want to create an S3 generic in the global environment from a function. I took a look at R.methodsS3::setGenericS3.default and came up with the following:

create_generic <- function(nm) {
  src <- sprintf("%s <- function(obj, ...) UseMethod(\"%s\")", nm, nm)
  expr <- parse(text = src)
  print(expr)
  eval(expr, env = .GlobalEnv)
}

create_generic("cat")
#> expression(cat <- function(obj, ...) UseMethod("cat"))
cat
#> function (obj, ...) 
#> UseMethod("cat")

This works how I'd like it to. However, I've been trying to make this work using quotation and I'm stuck:

library(rlang)

create_generic2 <- function(nm) {
  expr <- expr(!!sym(nm) <- function(obj, ...) UseMethod(!!nm))
  print(expr)
  eval(expr, env = .GlobalEnv)
}

create_generic2("dog")
#> dog <- function(obj, ...) UseMethod("dog")
dog
#> function(obj, ...) UseMethod(!!nm)

This makes use of tidyeval since that's what I'm familiar with, but I'd like to see what this looks like in base R.

I'm interested in any version of this that works without the string manipulation in create_generic.


Solution

  • In base R:

    create_generic <- function(fun_name) {
      new_fun <- function(obj, ...) UseMethod(fun_name)
      assign(fun_name, new_fun, envir = .GlobalEnv)
    }
    
    cat("hi\n")
    # hi
    
    create_generic("cat")
    
    cat("hi\n")
    # Error in UseMethod(fun_name) : 
    #   no applicable method for 'cat' applied to an object of class "character"
    
    cat.character <- base::cat
    
    cat("hi\n")
    # hi