[Rd] S4 classes and S3 generic functions

Gabor Grothendieck ggrothendieck at gmail.com
Mon Jun 14 01:35:23 CEST 2010

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