From jmc at research.bell-labs.com  Wed Aug 25 22:50:36 2004
From: jmc at research.bell-labs.com (John Chambers)
Date: Wed Aug 25 22:50:55 2004
Subject: [Rd] S3 classes in S4 class definitions
Message-ID: <412CFB9C.F890C6CF@research.bell-labs.com>

One of the changes some packages are  noticing from the recent
modifications to methods is warnings of "undefined classes" during
installation when the package extends S3 classes or uses them as slots
UNLESSS they have been declared in a setOldClass() call.  The green book
"strongly recommends" declaring S3 classs this way (pp 449-451).

The warnings on undeclared classes threatened in the book have now been
implemented ;-)  (However the statement in the book that S3 inheritance
is not used in dispatching S4 methods is not true.  Once the relevant
setOldClass call has been done, the inheritance should work.)

Since the packages using the S3 classes won't likely use any S4
methods,  it will be up to new packages to include the setOldClass()
calls, unless we try to provide some more systematic bridge for the old
code.

The text below is being added to the setClass() help file to articulate
some of the points.  Comments and suggestions welcome of course.

John

PS: I will be away for a few days, but should be back in e-mail contact
on Monday.

--------------------------
\section{S3 Classes}

     Earlier, informal classes of objects (usually referred to as "S3"
     classes) are used by many R functions.  It's natural to consider
     including them as the class for a slot in a formal class, or even
     as a class to be extended by the new class.  This isn't prohibited
     but there are some disadvantages, and if you do want to include S3
     classes, they should be declared by including them in a call to
     'setOldClass'.  Here are some considerations:

        *  Using S3 classes somewhat defeats the purpose of defining a
           formal class:  An important advantage to your users is that
           a formal class provides guarantees of what the object
           contains (minimally, the classes of the slots and therfore
           what data they contain; optionally, any other requirements
           imposed by a validity method).

           But there is no guarantee whatever about the data in an
           object from an S3 class.  It's entirely up to the functions
           that create or modify such objects.  If you want to provide
           guarantees to your users, you will need a valdity method
           that explicitly checks the contents of S3-class objects.

        *  To get the minimal guarantee (that the object in a slot has,
           or extends, the class for the slot) you should ensure that
           the S3 classes are known to _be_ S3 classes, with the
           possible inheritance.  To do this, include a call to
           'setOldClass' for the S3 classes used.

           Otherwise, the S3 class is undefined (and the code used by
           'setClass' will issue a warning).  Slot assignments, for
           example, will not then check for possible errors.

        *  These caveats apply to S3 classes; that is, objects with a
           class assigned by some R function but without a formal class
           definition.  In contrast, the  built-in data types
           ('numeric', 'list', etc.) are generally fine as slots or for
           'contains=' classes (see the previous section).  These data
           types don't have formal slots, but the base code in the
           system essentially forces them to contain the type of data
           they claim to have.

           The data types 'matrix' and 'array' are somewhat in between.
            They do not have an explicit S3 class, but do have one or
           two attributes.  There is no general problem in having these
           as slots, but because there is no guarantee of a dimnames
           slot, they don't work as formal classes.  The 'ts' class is
           treated as a formal class, extending class 'vector'.
----------------------------------

Plus an example of the form:

setOldClass(c("aov", "lm"))
setClass("lmCommented", representation(results = "lm", comments =
"character"))
aa = new("lmCommented", results = npk.aov, comments = "From the aov
example")
try(new("lmCommented", results = npk.aovE, comments = "BAD!!"))
### causes an error because npk.aovE is of class "aovlist"


-- 
John M. Chambers                  jmc@bell-labs.com
Bell Labs, Lucent Technologies    office: (908)582-2681
700 Mountain Avenue, Room 2C-282  fax:    (908)582-3340
Murray Hill, NJ  07974            web: http://www.cs.bell-labs.com/~jmc


[Rd] S3 classes in S4 class definitions
John Chambers