[Rd] S4: inheritance of validity methods?

Martin Morgan mtmorgan at fhcrc.org
Tue Aug 18 18:58:27 CEST 2009


Vitalie S. wrote:
> On Tue, 18 Aug 2009 15:42:48 +0200, Martin Morgan <mtmorgan at fhcrc.org>
> wrote:
> 
>> "Vitalie S." <vitosmail at rambler.ru> writes:
>>
>>> Dear Developers,
>>>
>>> In current implementation of validity method, objects are first
>>> coerced to  superclass (slots are striped). Thus, it is not possible
>>> to write validity  method which would perform some checks on children
>>> slots.
>>>
>>> Say, I want to check if number of slots in a class is equal to "n":
>>>
>>> setClass("A", representation(a="numeric", n="integer"),
>>>           prototype=list(a=12, n=1L),
>>>           validity=function(object){
>>>               if(length(slotNames(object))!=object at n+1) paste("Number
>>>               of  slots must be ", object at n)
>>>               else TRUE
>>>           })
>>>
>>>
>>> setClass("B", representation(b="numeric"), contains="A",
>>>           prototype=list(a=12, b=14, n=2L))
>>>
>>>
>>> new("B", a=11, b=33)
>>> Error in validObject(.Object) :
>>>    invalid class "B" object: Number of slots must be  2
>>>
>>> Error, because an object of class "A" is passed to validObject with
>>> one  slot "b" removed and n=2.
>>>
>>> Is were a work around for this, or I am just doomed to write the same
>>> validity method for each children class?
>>
>> I think you're doomed, but checking the number of slots in an instance
>> seems like a very unusual validity method -- isn't this the job of the
>> methods package, not your implementation of particular classes?

Perhaps I was too quick in saying you were doomed. First, if A is
VIRTUAL then the class seen by its validity method is, as implied by ?as,

  the object will be from the closest actual class, in particular the
  original object, if that class extends the virtual class directly

so

  .validity <- function(object) {
      cat(class(object), "\n")
      TRUE
  }

  setClass("A",
           representation(a="numeric", n="integer", "VIRTUAL"),
           validity=.validity)

  setClass("B", representation(b="numeric"), contains="A")

leads to

> res <- new("B", b=1)
B

This seems to require some careful thought about how derived classes are
to behave.

Another approach, probably obvious and which does not really bring
anything 'automatic', is to define a helper function that can be called
in each validity method, or itself used as the validity method.

  .validity <- function(object) {
     # TRUE or message
  }

  setClass("A", representation(a="numeric", n="integer"))
  setClass("B", representation(b="numeric"), contains="A",
           validity=.validity)
  setClass("C", representation(c="numeric"), contains="A",
           validity=function(object) .validity(object))

one might make .validity generic, so that one gets inheritance &
dispatch in the validity methods.

 setGeneric(".validity")
 setClass("A", representation(a="numeric", n="integer"))
 setClass("B", representation("A", b="numeric"), validity=.validity)
 setMethod(.validity, "A", function(object) {
    cat(class(object), "\n")
    TRUE
 })


> That was a toy example. In fact that I need is to ensure that some
> specific slots have the same length. Names of these slots are kept in
> separate slot (@atributes).
> 
> I think it's not an insane idea to require a predefined relationship to
> hold between "future" children's slots.
> 
> To be honest, I find it hard to think of a compelling reason to convert
> the object before testing the validity. What can possibly go wrong if
> children slots are kept in place?

I guess this is reflected in 'setIs' / 'setAs', which allow for derived
classes to have and interpret their representation in a way that is
completely different from parent classes.

Martin

> 
> 
> Vitalie.
> 
>> Martin
>>
>>> Many thanks,
>>> Vitalie.
>>
> 
>



More information about the R-devel mailing list