[Rd] S4 classes and S3 generic functions

John Chambers jmc at r-project.org
Mon Jun 14 02:47:10 CEST 2010

There are  number of examples in the documentation, as noted, 
illustrating different situations.

Here is one of them.  Of course, to "enter and run" it you need rev 52267.

setClass("classA", contains = "numeric",
    representation(realData = "numeric"))

Math.classA <- function(x) {(getFunction(.Generic))(x at realData)}
setMethod("Math", "classA", Math.classA)

x <- new("classA", log(1:10), realData = 1:10)

stopifnot(identical(abs(x), 1:10))

setClass("classB", contains = "classA")

y <- new("classB", x)

stopifnot(identical(abs(y), 1:10)) # (version 2.9.0 or earlier fails here)

## an S3 generic: just for demonstration purposes
f3 <- function(x, ...) UseMethod("f3")

f3.default <- function(x, ...) "Default f3"

## S3 method (only) for classA
f3.classA <- function(x, ...) "Class classA for f3"

## S3 and S4 method for numeric
f3.numeric <- function(x, ...) "Class numeric for f3"
setMethod("f3", "numeric", f3.numeric)

## The S3 method for classA and the closest inherited S3 method for classB
## are not found.

f3(x); f3(y) # both choose "numeric" method

## to obtain the natural inheritance, set identical S3 and S4 methods
setMethod("f3", "classA", f3.classA)

f3(x); f3(y) # now both choose "classA" method


On 6/13/10 4:35 PM, Gabor Grothendieck wrote:
> On Sun, Jun 13, 2010 at 6:58 PM, John Chambers<jmc at r-project.org>  wrote:
>> A general goal for the next version of R is to make S4 and S3 play better
>> together.
>> As mentioned in a previous thread, one limitation has been that S3 generic
>> functions, specifically the UseMethod() call, did not make use of S4
>> inheritance when dispatching on general S4 objects.
>> This has been fixed in a version committed today (updated to rev 52267).
>>   The code change is not large, but it has some general implications. Mainly,
>> in applying S3 generic functions to objects from S4 classes, the default
>> recommendation is to define an S3 method for the class, when possible, and
>> then set that definition to be the S4 method as well.
>> The section "Methods for S3 Generic Functions" of the ?Methods documentation
>> in the (new) version has details and points to examples. The text of that
>> section is appended below.
>> John
>> ======================
>> Methods for S3 Generic Functions:
>>      S4 methods may be wanted for functions that also have S3 methods,
>>      corresponding to classes for the first formal argument of an S3
>>      generic function-either a regular R function in which there is a
>>      call to the S3 dispatch function, 'UseMethod', or one of a fixed
>>      set of primitive functions, which are not true functions but go
>>      directly to C code. In either case S3 method dispatch looks at the
>>      class of the first argument or the class of either argument in a
>>      call to one of the primitive binary operators. S3 methods are
>>      ordinary functions with the same arguments as the generic function
>>      (for primitives the formal arguments are not actually part of the
>>      object, but are simulated when the object is printed or viewed by
>>      'args()'). The "signature" of an S3 method is identified by the
>>      name to which the method is assigned, composed of the name of the
>>      generic function, followed by '"."', followed by the name of the
>>      class. For details, see S3Methods.
>>      To implement a method for one of these functions corresponding to
>>      S4 classes, there are two possibilities: either an S4 method or an
>>      S3 method with the S4 class name. The S3 method is only possible
>>      if the intended signature has the first argument and nothing else..
>>      In this case, the recommended approach is to define the S3 method
>>      and also supply the identical function as the definition of the S4
>>      method. If the S3 generic function was 'f3(x, ...)' and the S4
>>      class for the new method was '"myClass"':
>>      f3.myClass<- function(x, ...) { ..... }
>>      setMethod("f3", "myClass", f3.myClass)
>>      The reasons for defining both S3 and S4 methods are as follows:
>>        1. An S4 method alone will not be seen if the S3 generic
>>           function is called directly.  However, primitive functions
>>           and operators are exceptions: The internal C code will look
>>           for S4 methods if and only if the object is an S4 object.  In
>>           the examples, the method for '`[`' for class '"myFrame"' will
>>           always be called for objects of this class.
>>           For the same reason, an S4 method defined for an S3 class
>>           will not be called from internal code for a non-S4 object.
>>           (See the example for function 'Math' and class '"data.frame"'
>>           in the examples.)
>>        2. An S3 method alone will not be called if there is _any_
>>           eligible non-default S4 method. (See the example for function
>>           'f3' and class '"classA"' in the examples.)
>>      Details of the selection computations are given below.
>>      When an S4 method is defined for an existing function that is not
>>      an S4 generic function (whether or not the existing function is an
>>      S3 generic), an S4 generic function will be created corresponding
>>      to the existing function and the package in which it is found
>>      (more precisely, according to the implicit generic function either
>>      specified or inferred from the ordinary function; see
>>      'implicitGeneric'). A message is printed after the initial call to
>>      'setMethod'; this is not an error, just a reminder that you have
>>      created the generic. Creating the generic explicitly by the call
>>      'setGeneric("f3")'
>>      avoids the message, but has the same effect. The existing function
>>      becomes the default method for the S4 generic function. Primitive
>>      functions work the same way, but the S4 generic function is not
>>      explicitly created (as discussed below).
>>      S4 and S3 method selection are designed to follow compatible rules
>>      of inheritance, as far as possible. S3 classes can be used for any
>>      S4 method selection, provided that the S3 classes have been
>>      registered by a call to 'setOldClass', with that call specifying
>>      the correct S3 inheritance pattern. S4 classes can be used for any
>>      S3 method selection; when an S4 object is detected, S3 method
>>      selection uses the contents of 'extends(class(x))' as the
>>      equivalent of the S3 inheritance (the inheritance is cached after
>>      the first call).
>>      An existing S3 method may not behave as desired for an S4
>>      subclass, in which case utilities such as 'asS3' and 'S3Part' may
>>      be useful.  If the S3 method fails on the S4 object, 'asS3(x)' may
>>      be passed instead; if the object returned by the S3 method needs
>>      to be incorporated in the S4 object, the replacement function for
>>      'S3Part' may be useful, as in the method for class '"myFrame"' in
>>      the examples.
>>      Here are details explaining the reasons for defining both S3 and
>>      S4 methods. Calls still accessing the S3 generic function directly
>>      will not see S4 methods, except in the case of primitive
>>      functions. This means that calls to the generic function from
>>      namespaces that import the S3 generic but not the S4 version will
>>      only see S3 methods. On the other hand, S3 methods will only be
>>      selected from the S4 generic function as part of its default
>>      ('"ANY"') method. If there are inherited S4 non-default methods,
>>      these will be chosen in preference to _any_ S3 method.
>>      S3 generic functions implemented as primitive functions (including
>>      binary operators) are an exception to recognizing only S3 methods..
>>      These functions dispatch both S4 and S3 methods from the internal
>>      C code. There is no explicit generic function, either S3 or S4.
>>      The internal code looks for S4 methods if the first argument, or
>>      either of the arguments in the case of a binary operator, is an S4
>>      object. If no S4 method is found, a search is made for an S3
>>      method.
>>      S4 methods can be defined for an S3 generic function and an S3
>>      class, but if the function is a primitive, such methods will not
>>      be selected if the object in question is not an S4 object. In the
>>      examples below, for instance, an S4 method for signature
>>      '"data.frame"' for function 'f3()' would be called for the S3
>>      object 'df1'. A similar S4 method for primitive function '`[`'
>>      would be ignored for that object, but would be called for the S4
>>      object 'mydf1' that inherits from '"data.frame"'. Defining both an
>>      S3 and S4 method removes this inconsistency.
> Would like to see one or more examples of this that one can enter into
> R and run.

More information about the R-devel mailing list