I was surprised to discover in the documentation for the rounding functions, e.g. round
, that they are all S4 generics. What benefits does this grant them over being S3 generics? As best as I can tell, everything that they do can already be done equally well if not better (I think that S3 dispatch is faster than S4?) in S3.
ceiling
, floor
, round
, signif
, and trunc
are all internally generic functions. You will not find corresponding generic function objects (of type "closure"
, calling UseMethod
or standardGeneric
) in the base or methods namespace, because dispatch happens entirely in C code for efficiency. The same can be said of the usual arithmetic, mathematical, logical, and relational operators.
You can define both S3 and S4 methods for, e.g., round
, and both can be dispatched. We already know about the S3 methods round.Date
and round.POSIXt
:
> round.Date
function (x, ...)
{
.Date(NextMethod(), oldClass(x))
}
<bytecode: 0x13130e7e8>
<environment: namespace:base>
> identical(round(.Date(0.1)), .Date(0))
[1] TRUE
And it is not too hard to devise an S4 method:
> setClass("zzz", contains = "character")
> x <- new("zzz", "0.1")
> round(x)
Error in round(x) : non-numeric argument to mathematical function
> setMethod("round", "zzz", function(x, digits = 0) round(as.double(x)))
> round(x)
[1] 0
Hence the statement in ?round
to which you refer
These are all (internally) S4 generic.
is a bit misleading. It says that you can define S4 methods for round
and that they are dispatched in C code, which is true, but it fails to express that you can also define S3 methods for round
and that those are also dispatched in C code.
I'll refrain from answering your broader question about the benefits of S4 over S3, because it has been discussed at length elsewhere (e.g., here). Besides, that discussion doesn't really apply to internally generic functions like round
, which are implemented exceptionally for speed.