[Rd] S4 classes and S3 generic functions

From: John Chambers <jmc_at_r-project.org>
Date: Sun, 13 Jun 2010 15:58:16 -0700


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. ______________________________________________
R-devel_at_r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel Received on Sun 13 Jun 2010 - 23:01:15 GMT

Archive maintained by Robert King, hosted by the discipline of statistics at the University of Newcastle, Australia.
Archive generated by hypermail 2.2.0, at Mon 14 Jun 2010 - 00:51:08 GMT.

Mailing list information is available at https://stat.ethz.ch/mailman/listinfo/r-devel. Please read the posting guide before posting to the list.

list of date sections of archive