From jmc at r-project.org  Mon Jun 14 02:47:10 2010
From: jmc at r-project.org (John Chambers)
Date: Sun, 13 Jun 2010 17:47:10 -0700
Subject: [Rd] S4 classes and S3 generic functions
In-Reply-To: <AANLkTimsC1z7stkwRR4TcEflZ8mOp_27sTXeT_WJwusp@mail.gmail.com>
References: <4C156288.2030207@r-project.org>
	<AANLkTimsC1z7stkwRR4TcEflZ8mOp_27sTXeT_WJwusp@mail.gmail.com>
Message-ID: <4C157C0E.2090907@r-project.org>

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.
>



[Rd] S4 classes and S3 generic functions
John Chambers