Re: [R] inheritence in S4

From: Martin Morgan <mtmorgan_at_fhcrc.org>
Date: Mon, 03 Mar 2008 09:27:16 -0800

Hi Christophe --

This is a variant of the problem that Jim Regetz is having in a thread in R-devel. Here's where the trouble is

> as(c, "A")

Error in .local(.Object, ...) :
  argument "value" is missing, with no default

By default, 'as(c, "A")' will create a new instance of it's second argument using new("A"), and then fill the slots of A with appropriate values from "C". We can see that creating a new "A" without any additional arguments causes the same error:

> new("A")

Error in .local(.Object, ...) :
  argument "value" is missing, with no default

Jim has gone down the path of creating coercion methods ('setAs') for his classes. A different solution is to ensure that 'new' works with no additional arguments (typically requiring that a prototype, if present, prodcues valid objects). So for instance

setMethod("initialize","A",function(.Object, value=numeric(0)){

    .Object_at_x <- value
    return(.Object)
})

and then

> new("A")

A

I find it easier to keep track of prototype and initialize methods, rather than setAs, so I use a solution like the above. But a couple of other quick points. I would have written

setMethod("initialize", "A",

          function(.Object, ..., xValue=numeric(0)){
              callNextMethod(.Object, ..., x=xValue)
          })

Why? this allows the built-in object creation methods to create .Object, so there's less code for me to maintain (even if it's just object assignment .Object_at_x <- value here). Importantly, when I create a derived class, the derived class does not have to know in detail about what the initilalize method for "A" does, e.g.,

setMethod("initialize","B",

          function(.Object, ..., yValue=numeric(0)){
              callNextMethod(.Object, ..., y=yValue)
          })

Here 'initialize' for B just deals with it's slots, and doesn't have to worry about what to do with A's slots. Also .Object_at_x <- value makes a copy of .Object, which can be expensive if .Object is large. There is some hope that the default method (eventually reached by callNextMethod) does things relatively efficiently in terms of copies. Note that each initialize method only deals with its own slots. And finally, the position of 'xValue' and 'yValue' means that the arugment has to be named, e.g., new("B", yValue=12). This seems a little awkward at first, but seems like a best practice when creating objects with complicated inheritance -- not quite so much need to follow the method dispatch / argument assignment rules through a complicated inheritance hierarchy.

And finally, in Jim's thread I mention using a constructor. So in practice for a case like the above I would not define any initialize methods, and instead write

B <- function(xValue=numeric(0), yValue=numeric(0)) {

    new("B", x=xValue, y=yValue)
}

All my slot coercion is in the constructor. The user can figure out from the signature of the constructor what the appropriate arguments and their types are, and does not have to know about the details of the class definition. I can catch common errors and provide user-friendly messages, rather than getting cryptic messages from the internals of S4.

Hope that helps.

Martin

Christophe Genolini <cgenolin_at_u-paris10.fr> writes:

> Thanks Martin
>
> Well it works except that "as" seems to not like the "initialize"
> method : the following code (that is the same than yours with some
> initialize for A B and C) does not compile. It seems that as(c,"A")
> does not work if we definie a initialize for A...
>
> --- 8< --------------
> setClass("A", representation(x="numeric"))
> setMethod("initialize","A",function(.Object,value){.Object_at_x <-
> value;return(.Object)})
> a <- new("A",4)
>
> setClass("B", representation(y="numeric"))
> setMethod("initialize","B",function(.Object,value){.Object_at_y <-
> value;return(.Object)})
> b <- new("B",5)
>
> setClass("C", contains=c("A", "B"))
> setMethod("initialize","C",function(.Object,valueA, valueB){
> .Object_at_x <- valueA
> .Object_at_y <- valueB
> return(.Object)
> })
> c <- new("C",valueA=10,valueB=12)
>
> setMethod("show", "A", function(object) cat("A\n"))
> setMethod("show", "B", function(object) cat("B\n"))
> setMethod("show", "C", function(object) {
> callGeneric(as(object, "A"))
> callGeneric(as(object, "B"))
> cat("C\n")
> })
> c
> --- 8< --------------------
>
> Is there something wrong with the use of 'as' between class and father
> class?
>
> Christophe
>> Hi Christophe --
>>
>> I don't know whether there's a particularly elegant way. This works
>>
>> setClass("A", representation(x="numeric"))
>> setClass("B", representation(y="numeric"))
>> setClass("C", contains=c("A", "B"))
>>
>> setMethod("show", "A", function(object) cat("A\n"))
>> setMethod("show", "B", function(object) cat("B\n"))
>> setMethod("show", "C", function(object) {
>> callGeneric(as(object, "A"))
>> callGeneric(as(object, "B"))
>> cat("C\n")
>> })
>>
>>
>>> new("C")
>>>
>> A
>> B
>> C
>>
>> but obviously involves the developer in making explicit decisions
>> about method dispatch when there is multiple inheritance.
>>
>> Martin
>>
>> cgenolin_at_u-paris10.fr writes:
>>
>>
>>> Hi the list
>>>
>>> I define a class A (slot a and b), a class C (slot c and d) and a
>>> class E that inherit from A and B.
>>> I define print(A) and print(B). For print(C), I would like to use
>>> both of them, but I do not see how...
>>>
>>> Thanks for your help...
>>>
>>> Christophe
>>>
>>> ----------------------------------------------------------------
>>> Ce message a ete envoye par IMP, grace a l'Universite Paris 10 Nanterre
>>>
>>> ______________________________________________
>>> R-help_at_r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-help
>>> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
>>> and provide commented, minimal, self-contained, reproducible code.
>>>
>>
>>
>

-- 
Martin Morgan
Computational Biology / Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N.
PO Box 19024 Seattle, WA 98109

Location: Arnold Building M2 B169
Phone: (206) 667-2793

______________________________________________
R-help_at_r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.
Received on Mon 03 Mar 2008 - 17:31:57 GMT

Archive maintained by Robert King, hosted by the discipline of statistics at the University of Newcastle, Australia.
Archive generated by hypermail 2.2.0, at Tue 04 Mar 2008 - 11:30:19 GMT.

Mailing list information is available at https://stat.ethz.ch/mailman/listinfo/r-help. Please read the posting guide before posting to the list.

list of date sections of archive