Search code examples
rgenericsmethods

Pass local variables to methods in R >= 4.4.0


From ?UseMethod (also in the NEWS file):

UseMethod creates a new function call with arguments matched as they came in to the generic. [Previously local variables defined before the call to UseMethod were retained; as of R 4.4.0 this is no longer the case.]

With this dummy generic and its methods:

f <- function(x) {
  y <- head(x, 1)
  z <- tail(x, 1)
  UseMethod("f")
}

f.numeric <- function(x) {
  x + y + z
}

f.character <- function(x) {
  paste(x, y, z)
}

In R >= 4.4.0, we have:

f(1:3)
# Error in f.numeric(1:3) : object 'y' not found

f(c("a", "b", "c"))
# Error in f.character(c("a", "b", "c")) : object 'y' not found

While in R < 4.4.0, we had:

f(1:3)
# [1] 5 6 7

f(c("a", "b", "c"))
# [1] "a a c" "b a c" "c a c"

What is now the recommended way to pass local variables to methods?

My use case involves several objects that are precomputed from x before the call to UseMethod(). I am wondering if there is an alternative to wrapping this step in a function that would be called in each method, e.g.:

f <- function(x) {
  UseMethod("f")
}

g <- function(x) {
  y <- head(x, 1)
  z <- tail(x, 1)
  list(y = y, z = z)
}

f.numeric <- function(x) {
  yz <- g(x)
  x + yz$y + yz$z
}

f.character <- function(x) {
  yz <- g(x)
  paste(x, yz$y, yz$z)
}

This remain less practical than the former behavior because objects are now nested in a list. Anyway, I wish to avoid code duplication in the methods because the common step is relatively long (in LOC not time).


Solution

  • How about this?

    f <- function(x) {
      y <- head(x, 1)
      z <- tail(x, 1)
      f_int(x, y, z)
    }
    
    f_int <- function(x, ...)   UseMethod("f_int")
    
    f_int.numeric <- function(x, y, z, ...) {
      x + y + z
    }
    
    f_int.character <- function(x, y, z, ...) {
      paste(x, y, z)
    }
    
    f(1:3)
    #[1] 5 6 7
    
    f(c("a", "b", "c"))
    #[1] "a a c" "b a c" "c a c"