What is the purpose/effect of providing the 'generic' argument to `NextMethod()`?

62 views Asked by At

I am trying to understand what the purpose of the generic argument of NextMethod() is.

Example

# example data
x <- as.Date(c("2022-01-01", "2023-01-01", "1900-01-01", "2024-01-01")) 

# gives cumulative maximum as expected
cummax.Date <- function(x, ...) .Date(NextMethod(), cl = oldClass(x))
cummax(x)
#> [1] "2022-01-01" "2023-01-01" "2023-01-01" "2024-01-01"

# gives cumulative minimum as expected
cummin.Date <- function(x, ...) .Date(NextMethod(), cl = oldClass(x))
cummin(x)
#> [1] "2022-01-01" "2022-01-01" "1900-01-01" "1900-01-01"

Best Practice

Quite often, the generic is called explicitely.

cummin.Date <- function(x, ...) .Date(NextMethod(generic = "cummin"), cl = oldClass(x))
cummin(x)
#> [1] "2022-01-01" "2022-01-01" "1900-01-01" "1900-01-01"

Bad Practice - no Difference

However, explicitly calling the generic does not impact the result whatsoever, no matter if it exists or not.

# still returns the cumulative minimum
cummin.Date <- function(x, ...) .Date(NextMethod(generic = "cummax"), cl = oldClass(x))
cummin(x)
#> [1] "2022-01-01" "2022-01-01" "1900-01-01" "1900-01-01"

# still returns the cumulative minimum
cummin.Date <- function(x, ...) .Date(NextMethod(generic = "foobar"), cl = oldClass(x))
cummin(x)
#> [1] "2022-01-01" "2022-01-01" "1900-01-01" "1900-01-01"
1

There are 1 answers

0
Mikael Jagan On BEST ANSWER

NextMethod uses argument generic only if it does not find the symbol .Generic in the calling environment of the method. That happens only in atypical usage:

> .S3method("mean", "a", function(x, ...) { rm(.Generic); NextMethod() })
> mean(structure(1:6, class = "a"))
Error in NextMethod() : generic function not specified
> .S3method("mean", "b", function(x, ...) { rm(.Generic); NextMethod("diff") })
> mean(structure(1:6, class = "b"))
[1] 1 1 1 1 1
attr(,"class")
[1] "b"
> .S3method("mean", "c", function(x, ...) { .Generic <- "diff"; NextMethod() })
> mean(structure(1:6, class = "c"))
[1] 1 1 1 1 1
attr(,"class")
[1] "c"

It is undocumented (I think), but visible in the sources of the internal do_nextmethod, which uses readS3VarsFromFrame (defined elsewhere) to locate .Generic.

I don't think that anyone would recommend assigning to .Generic, removing .Generic, or otherwise calling NextMethod in a context where .Generic is not there. Indeed, I think that the advice in help("NextMethod") about .Class applies here:

Note that .Class is set when the generic is called, and is unchanged if the class of the dispatching argument is changed in a method. It is possible to change the method that NextMethod would dispatch by manipulating .Class, but 'this is not recommended unless you understand the inheritance mechanism thoroughly' (Chambers & Hastie, 1992, p. 469).