[Rd] Why S4 methods of S3 'base' generics are not used in 'base' functions ?
John Chambers
jmc at r-project.org
Wed Mar 18 19:13:13 CET 2009
The short answer is because S3 method dispatch knows nothing about S4
methods and never has (but maybe should). You select S4 methods by
creating and calling an S4 generic outside of base, and base functions
don't call it.
Details:
Your assertion is not entirely correct. As always, you need to look at
the individual function.
There are two different situations. While, e.g., both `+` and as.matrix
can have S3 or S4 methods, the mechanism is different.
R(r48116)> `+`
function (e1, e2) .Primitive("+")
R(r48116)> as.matrix
function (x, ...)
UseMethod("as.matrix")
<environment: namespace:base>
In either case, S4 methods will be dispatched _if_ the package involved
has correctly defined them, but the mechanism is different, and in one
case requires the call to come from somewhere that sees the S4 methods.
For primitives, no explicit S4 generic function is defined. Instead,
the underlying C code dispatches S4 methods once a package has "turned
on" methods for that function.
For S3 generics, such as as.matrix, the setMethod() in the package
creates an S4 generic. This function calls for the S4 method dispatch.
The object base::as.matrix is still an S3 generic. While it might be
good for UseMethod() to recognize that S4 methods exist, it doesn't and
won't for 2.9.0.
The problem with the call to scale() is then that scale.default calls
as.matrix() _from the base namespace_ (as it should) and the resulting
UseMethod() doesn't dispatch the S4 method (where I tend to agree with
you that it sensibly should, given that x is an S4 object). To see the
behavior, trace the two versions:
R(r48116)> find("as.matrix")
[1] ".GlobalEnv" "package:base"
R(r48116)> trace(as.matrix) ## in global env.
R(r48116)> y <- scale(x) ## no trace() output
R(r48116)> trace(base::as.matrix) ## in base
R(r48116)> y <- scale(x)
trace: as.matrix(x)
R(r48116)>
Until UseMethod() does S4 dispatch, you will need to supply base
functions with objects they understand, as in your second call to scale().
Without taking back my diatribe on S3 methods for S4 classes, I believe
they now do work again for R 2.9.0, so you can decide whether you should
revise and how.
John
Yohan Chalabi wrote:
> Dear list,
>
> It seems that S4 methods defined for an S3 'base' generic
> are not used in 'base' functions.
>
> This can be problematic when 'base' functions start with
> something like 'as.matrix'.
>
>
> ### START R code
>
> setClass("classA", contains = "matrix",
> representation(realData = "numeric"))
>
> setMethod("as.matrix", "classA", function(x) callGeneric(x at realData))
>
> x <- new("classA", diag(1:4), realData = 1:4)
>
> as.matrix(x)
>
> ## # as intended
> ## [,1]
> ## [1,] 1
> ## [2,] 2
> ## [3,] 3
> ## [4,] 4
>
> # but as.matrix in 'base' functions dispatches to the default S3
> # method rather than to the S4 method defined above.
> scale(x)
> scale(as.matrix(x))
>
> # Note that S4 methods are well dispatched for functions which are
> # not S3 generics.
> setMethod("dimnames", "classA",
> function(x) list(NULL, as.character(x at realData)))
> dimnames(x)
>
> solve(x) # here row names are properly assigned thanks to the 'dimnames'
> # method defined above.
>
> ### END R code
>
> What is your recommended solution to make S4 methods of S3 'base'
> generics work in 'base' functions?
>
> A solution could be to overwrite 'as.matrix' in '.Load' and force it to
> use the S4 method with S4 objects. But doing so looks to me rather
> dangerous because it would lead to conflicts between packages.
>
> Another solution could be to define S3 methods. But, as it has been
> already explained on the list, it is a design error.
>
> Thanks in advance for any suggestion!
>
> Best regards,
> Yohan
>
>
>
More information about the R-devel
mailing list