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

67 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 best solutions below

0
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).