As Yohan points out, and as we found in testing CRAN packages, there are 
a number of examples where programmers have written S3 methods for S4 
classes, such as print.aTest() below.

This may well have seemed an easy and convenient mechanism, but it is a 
design error with potentially disastrous consequences.  Note that this 
is fundamentally different from an S3 method defined for an S3 class and 
inherited by an S4 class that extends the S3 class.

We haven't decided whether to re-enable this, but if so it will be with 
a warning at user call and/or at package check time.

Please turn such methods into legitimate S4 methods.  Usually the change 
is a simple call to setMethod(); in some cases, such as this example you 
may need a bit more work, such as a show() method to call the print.* 
function.

DETAILS:

There are at least two serious flaws in this mechanism, plus some minor 
defects.

1. S3 dispatch does not know about S4 class inheritance.
  So if as a user of Yohan's code, for example, I define  a class
    setClass("bTest", contains = "aTest")
then that class will not inherit any of the S3 methods.  In the case of 
print, that will be obvious.  The disaster waiting to happen is when the 
method involves numerical results, in which case I may be getting 
erroneous results, with no hint of the problem.

2. Conversely, S4 dispatch does not know about the S3 method.
 So, if my new class was:
    setClass("bTest", contains = c("aTest", "waffle7")
suppose "waffle7" has some huge inheritance, in the midst of which is a 
method for a generic function of importance.  I expect to inherit the 
method for "aTest" because it's for a direct superclass, but I won't, no 
matter how far up the tree the other method occurs.  (Even if point 1 
were fixed)

    Again, this would be obvious for a print method, but potentially 
disastrous elsewhere.

There are other minor issues, such as efficiency:  the S3 method 
requires two dispatches and perhaps may do some extra copying.  But 1 
and 2 are the killers.

Just to anticipate a suggestion:  Yes, we could perhaps fix 1 by adding 
code to the S3 dispatch, but this would ambiguate the legitimate attempt 
to handle inherited valid S3 methods correctly.

John

Yohan Chalabi wrote:
>>>>> "JC" == John Chambers <j...@r-project.org>
>>>>> on Fri, 06 Mar 2009 14:12:00 -0800
>>>>>           
>
>    JC> Some modifications have been committed for the r-devel
>    JC> version today
>    JC> that modify (essentially, correct a bug in) the communication
>    JC> of objects
>    JC> to an S3 method from an S4 class that extends the S3 class.
>    JC>
>    JC> This is one of a sequence of changes designed to make S4
>    JC> classes work
>    JC> more generally and consistently with S3 methods and classes.
>    JC>
>    JC> In 2.8.0, support was provided for S4 classes that extend
>    JC> S3 classes,
>    JC> partly by making S3 method dispatch recognize the inheritance.
>    JC>
>    JC> The catch was that the S3 method would get the S4 object.
>    JC> Two problems
>    JC> with that:
>    JC>
>    JC> 1. The S3 method would fail if it tried to use the S3 class
>    JC> information
>    JC> directly, since the class attribute was the S4 class.
>    JC>
>    JC> 2. More seriously, if the method used the object, modified
>    JC> it and
>    JC> returned the result, it had a good chance of returning an
>    JC> invalid object
>    JC> seeming to come from the S4 class.
>    JC>
>    JC> The modification to deal with this now delivers to the S3
>    JC> method the
>    JC> inherited S3 object.  (This turned out to be somewhat harder
>    JC> than the
>    JC> original change, since it impacts several pieces of internal
>    JC> code.)  A
>    JC> revision of the function asS4() deals with similar concerns--see
>    JC> the
>    JC> documentation.
>    JC>
>    JC> The change does not affect default methods.  It would be
>    JC> tempting to
>    JC> convert S4 objects for those, but some S3 generics attempt to
>    JC> deal with
>    JC> S4 objects, e.g., str().  A change to the primitives that
>    JC> dispatch
>    JC> methods is more plausible, but for the moment all that was
>    JC> added was
>    JC> more explicit error messages if a non-vector S4 object is
>    JC> supplied.
>    JC>
>    JC> For more information see the section on inheriting from
>    JC> non-S4 classes
>    JC> in the documentation ?Classes.
>    JC>
>    JC> It would be helpful if package maintainers would check this
>    JC> and previous
>    JC> changes by running their code against the r-devel version of
>    JC> R, before
>    JC> that becomes 2.9.0.  Please report any new errors (provided,
>    JC> of course,
>    JC> that the same code works with 2.8.1).
>    JC>
>    JC> John
>    JC>
>   
>
> Dear John,
>
> it seems that S3 methods for an S4 class which extents a
> matrix cannot be defined as it used to be the case in 2.8.1.
>
> For example
>
> ## code
> setClass("aTest",
>          representation(.Data = "matrix",
>                         comment = "character"))
>
>
> c1 <- new("aTest", .Data = matrix(1:4, ncol = 2), comment = "aTest")
>
> # it seems that it is no longer possible to define an S3
> # method for the class aTest
>
> print.aTest <- function(x, ...)
> {
>     cat("\n", x...@comment, "\n")
>     print(getDataPart(x))
> }
>
> print.aTest(c1)
> print(c1) # works in 2.8.1
>
> # another example could be
> as.matrix.aTest <- function(x, ...) getDataPart(x)
>
> as.matrix.aTest(c1)
> as.matrix(c1) # works in 2.8.1
>
> ## end code
>
>
> Is this the expected behavior?
>
>
> Best regards,
> Yohan
>
>   

        [[alternative HTML version deleted]]

______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

Reply via email to