[Rd] S4 classes and S3 generic functions

John Chambers jmc at r-project.org
Mon Jun 14 00:58:16 CEST 2010


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.



More information about the R-devel mailing list