[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