is                  package:methods                  R Documentation

Is an Object from a Class

Description:

     Functions to test inheritance relationships between an object and
     a class ('is') or between two classes ('extends'), and to
     establish such relationships ('setIs',  an explicit alternative to
     the 'contains=' argument to 'setClass').

Usage:

     is(object, class2)

     extends(class1, class2, maybe=TRUE, fullInfo = FALSE)

     setIs(class1, class2, test=NULL, coerce=NULL, replace=NULL,
           by = character(), where = topenv(parent.frame()), classDef =,
           extensionObject = NULL, doComplete = TRUE)

Arguments:

  object: any R object.

class1, class2: the names of the classes between which 'is' relations
          are to be examined defined, or (more efficiently) the class
          definition objects for the classes.

maybe, fullInfo: In a call to 'extends', 'maybe' is the value returned
          if a relation is conditional. In a call with 'class2'
          missing, 'fullInfo' is a flag, which if 'TRUE' causes a list
          of objects of class 'classExtension' to be returned, rather
          than just the names of the classes. 

test, coerce, replace: In a call to 'setIs', functions optionally
          supplied to test whether the relation is defined, to coerce
          the object to 'class2', and to alter the object so that
          'is(object, class2)' is identical to 'value'. See the details
          section below.

          The remaining arguments are for internal use and/or usually
          omitted.

extensionObject: alternative to the 'test, coerce, replace, by'
          arguments; an object from class 'SClassExtension' describing
          the relation.  (Used in internal calls.) 

doComplete: when 'TRUE', the class definitions will be augmented with
          indirect relations as well.  (Used in internal calls.)

      by: In a call to 'setIs', the name of an intermediary class. 
          Coercion will proceed by first coercing to this class and
          from there to the target class.  (The intermediate coercions
          have to be valid.)

   where: In a call to 'setIs', where to store the metadata defining
          the relationship. Default is the global environment for calls
          from the top level of the session or a source file evaluated
          there.  When the call occurs in the top level of a file in
          the source of a package, the default will be the name space
          or environment of the package.  Other uses are tricky and not
          usually a good idea, unless you really know what you are
          doing.

classDef: Optional class definition for 'class' , required internally
          when 'setIs' is called during the initial definition of the
          class by a call to 'setClass'. _Don't_ use this argument,
          unless you really know why you're doing so.

Summary of Functions:


     'is': With two arguments, tests whether 'object' can be treated as
          from 'class2'.

          With one argument, returns all the super-classes of this
          object's class.

     'extends': Does the first class extend the second class? The call
          returns 'maybe' if the extension includes a test.  

          When called with one argument, the value is a vector of the
          superclasses of 'class1'.  If argument 'fullInfo'  is 'TRUE',
          the call returns a named list of objects of class
          'SClassExtension'; otherwise, just the names of the
          superclasses.

     'setIs': Defines 'class1' to be an extension (subclass) of
          'class2'. If 'class2' is an existing virtual class, such as a
          class union, then only the two classes need to be supplied in
          the call, if the implied inherited methods work for 'class1'.
          See the details section below.

          Alternatively, arguments 'coerce' and 'replace' should be
          supplied, defining methods to coerce to the superclass and to
          replace the part corresponding to the superclass. As
          discussed in the details and other sections below, this form
          is often less recommended than the corresponding call to
          'setAs', to which it is an alternative. 


Details:

     Arranging for a class to inherit from another class is a key tool
     in programming.  In R, there are three basic techniques, the first
     two providing what is called  "simple" inheritance, the preferred
     form:


        1.  By the 'contains=' argument in a call to 'setClass'.  This
           is and should be the most common mechanism.  It arranges
           that the new class contains all the structure of the
           existing class, and in particular all the slots with the
           same class specified.  The resulting class extension is
           defined to be 'simple', with important implications for
           method definition (see the section on this topic below).

        2.  Making 'class1' a subclass of a virtual class either by a
           call to 'setClassUnion' to make the subclass a member of a
           new class union, or by a call to 'setIs' to add a class to
           an existing class union or as a new subclass of an existing
           virtual class.  In either case, the implication should be
           that methods defined for the class union or other superclass
           all work correctly for the subclass.  This may depend on
           some similarity in the structure of the subclasses or simply
           indicate that the superclass methods are defined in terms of
           generic functions that apply to all the subclasses.  These
           relationships are also generally simple.

        3.  Supplying 'coerce'  and 'replace' arguments to 'setAs'. R
           allows arbitrary inheritance relationships, using the same
           mechanism for defining coerce methods by a call to 'setAs'. 
           The difference between the  two is simply that 'setAs' will
           require a call to 'as' for a conversion to take place,
           whereas after the call to 'setIs', objects will be
           automatically converted to the superclass.

           The automatic feature is the dangerous part, mainly because
           it results in the subclass potentially inheriting methods
           that do not work.  See the section on inheritance below.  If
           the two classes involved do not actually inherit a large
           collection of methods, as in the first example below, the
           danger may be relatively slight.

           If the superclass inherits methods where the subclass has
           only a default or remotely inherited method, problems are
           more likely. In this case, a general recommendation is to
           use the 'setAs' mechanism instead, unless there is a strong
           counter reason. Otherwise, be prepared to override some of 
           the methods inherited.


     With this caution given, the rest of this section describes what
     happens when 'coerce=' and 'replace=' argument are supplied to
     'setIs'.

     The 'coerce' and 'replace' arguments are functions that define how
     to coerce a 'class1' object to 'class2', and how to replace the
     part of the subclass object that corresponds to 'class2'.  The
     first of these is a function of one argument (conventionally
     'from') and the second of two arguments ('from', 'value').  For
     details, see the section on coerce functions below .

     When 'by' is specified, the coerce process first coerces to this
     class and then to 'class2'.  It's unlikely you would use the 'by'
     argument directly, but it is used in defining cached information
     about classes.

     The value returned (invisibly) by 'setIs' is the revised class
     definition of 'class1'.

Coerce, replace, and test functions:

     The  'coerce' argument is a function that turns a 'class1' object
     into a 'class2' object.  The 'replace' argument is a function of
     two arguments that modifies a 'class1' object (the first argument)
     to replace the part of it that corresponds to 'class2' (supplied
     as 'value', the second argument).  It then returns the modified
     object as the value of the call.  In other words, it acts as a
     replacement method to implement the expression 'as(object, class2)
     <- value'.

     The easiest way to think of the  'coerce' and 'replace' functions
     is by thinking of the case that  'class1' contains 'class2' in the
     usual sense, by including the slots of the second class.  (To
     repeat, in this situation you would not call 'setIs', but the
     analogy shows what happens when you do.)

     The 'coerce' function in this case would just make a 'class2'
     object by extracting the corresponding slots from the 'class1'
     object. The 'replace' function would replace in the 'class1'
     object the slots corresponding to 'class2', and return the
     modified object as its value.

     For additional discussion of these functions, see the
     documentation of the 'setAs' function.  (Unfortunately, argument
     'def' to that function corresponds to argument 'coerce' here.) 

     The inheritance relationship can also be conditional, if a
     function is supplied as the 'test' argument.  This should be a
     function of one argument that returns 'TRUE' or 'FALSE' according
     to whether the object supplied satisfies the relation 'is(object,
     class2)'. Conditional relations between classes are slightly
     deprecated because they cannot be implemented as efficiently as
     ordinary relations and because they sometimes can lead to
     confusion (in thinking about what methods are dispatched for a
     particular function, for example).  But they can correspond to
     distinctions such as two classes that have the same
     representation, but with only one of them obeying certain
     additional constraints.

Inherited methods:

     A method written for a particular signature (classes matched to
     one or more formal arguments to the function) naturally assumes
     that the objects corresponding to the arguments can be treated as
     coming from the corresponding classes.  The objects will have all
     the slots and available methods for the classes.

     The code that selects and dispatches the methods ensures that this
     assumption is correct.  If the inheritance was "simple", that is,
     defined by one or more uses of the 'contains=' argument in a call
     to 'setClass', no extra work is generally needed.  Classes are
     inherited from the superclass, with the same definition.

     When inheritance is defined by a general call to 'setIs', extra
     computations are required.  This form of inheritance implies that
     the subclass does _not_ just contain the slots of the superclass,
     but instead requires the explicit call to the coerce and/or
     replace method.  To ensure correct computation, the inherited
     method is supplemented by calls to 'as' before the body of the
     method is evaluated.

     The calls to 'as' generated in this case have the argument 'strict
     = FALSE', meaning that extra information can be left in the
     converted object, so long as it has all the appropriate slots. 
     (It's this option that allows simple subclass objects to be used
     without any change.)  When you are writing your coerce method, you
     may want to take advantage of that option.

     Methods inherited through non-simple extensions can result in
     ambiguities or unexpected selections.  If 'class2' is a
     specialized class with just a few applicable methods, creating the
     inheritance relation may have little effect on the behavior of
     'class1'. But if 'class2' is a class with many methods, you may
     find that you now inherit some undesirable methods for 'class1',
     in some cases, fail to inherit expected methods. In the second
     example below, the non-simple inheritance from class '"factor"'
     might be assumed to inherit S3 methods via that class.  But the S3
     class is ambiguous, and in fact is '"character"' rather than
     '"factor"'.

     For some generic functions, methods inherited by non-simple
     extensions are either known to be invalid or sufficiently likely
     to be so that the generic function has been defined to exclude
     such inheritance.  For example 'initialize' methods must return an
     object of the target class; this is straightforward if the
     extension is simple, because no change is made to the argument
     object, but is essentially impossible.  For this reason, the
     generic function insists on only simple extensions for
     inheritance.  See the 'simpleInheritanceOnly' argument to
     'setGeneric' for the mechanism.  You can use this mechanism when
     defining new generic functions.

     If you get into problems with functions that do allow non-simple
     inheritance, there are two basic choices.  Either  back off from
     the 'setIs' call and settle for explicit coercing defined by a
     call to 'setAs'; or, define explicit methods involving 'class1' to
     override the bad inherited methods.  The first choice is the
     safer, when there are serious problems.

References:

     Chambers, John M. (2008) _Software for Data Analysis: Programming
     with R_ Springer.  (For the R version.)

     Chambers, John M. (1998) _Programming with Data_ Springer (For the
     original S4 version.)

Examples:

     ## Two examples of setIs() with coerce= and replace= arguments
     ## The first one works fairly well, because neither class has many
     ## inherited methods do be disturbed by the new inheritance

     ## The second example does NOT work well, because the new superclass,
     ## "factor", causes methods to be inherited that should not be.

     ## First example:
     ## a class definition (see setClass for class "track")
     setClass("trackCurve", contains = "track",
              representation( smooth = "numeric"))
     ## A class similar to "trackCurve", but with different structure
     ## allowing matrices for the "y" and "smooth" slots
     setClass("trackMultiCurve",
              representation(x="numeric", y="matrix", smooth="matrix"),
              prototype = structure(list(), x=numeric(), y=matrix(0,0,0),

                                    smooth= matrix(0,0,0)))
     ## Automatically convert an object from class "trackCurve" into
     ## "trackMultiCurve", by making the y, smooth slots into 1-column matrices
     setIs("trackCurve",
           "trackMultiCurve",
           coerce = function(obj) {
             new("trackMultiCurve",
                 x = obj@x,
                 y = as.matrix(obj@y),
                 smooth = as.matrix(obj@smooth))
           },
           replace = function(obj, value) {
             obj@y <- as.matrix(value@y)
             obj@x <- value@x
             obj@smooth <- as.matrix(value@smooth)
             obj})



     ## Second Example:
     ## A class that adds a slot to "character"
     setClass("stringsDated", contains = "character", representation(stamp="POSIXt"))

     ## Convert automatically to a factor by explicit coerce 
     setIs("stringsDated", "factor",
        coerce = function(from){factor(from@.Data)}, 
        replace=function(from, value){
            from@.Data<- as.character(value); from})


     ll <- sample(letters, 10, replace = TRUE)
      ld <- new("stringsDated", ll, stamp = Sys.time())

     levels(as(ld, "factor"))

     levels(ld) # will be NULL--see comment in section on inheritance above.

     ## In contrast, a class that simply extends "factor" has no such ambiguities

     setClass("factorDated", contains = "factor", representation(stamp="POSIXt"))

     fd = new("factorDated", factor(ll), stamp = Sys.time())

     identical(levels(fd), levels(as(fd, "factor")))

