From Peter.Ruckdeschel at uni-bayreuth.de  Mon Apr 10 11:07:27 2006
From: Peter.Ruckdeschel at uni-bayreuth.de (Peter Ruckdeschel)
Date: Mon, 10 Apr 2006 11:07:27 +0200
Subject: [Rd] setIs and method dispatch in S4 classes
Message-ID: <443A204F.4010701@uni-bayreuth.de>

Hi Seth ,

thank you for your reply.

Seth Falcon  <sfalcon at fhcrc.org> writes:

>Peter Ruckdeschel <Peter.Ruckdeschel at uni-bayreuth.de> writes:
>  
>
>> ## now: B00 mother class to B01 and B02, and again B02 "contains" B01 by
>> setIs:
>> setClass("B00", representation(a="numeric"))
>> setClass("B01", representation(a="numeric",b="numeric"), contains= "B00")
>> setClass("B02", representation(a="numeric",d="numeric"), contains= "B00")
>> setIs("B02","B01",coerce=function(obj){new("B01", a=obj at a, b=obj at d)},
>>        replace=function(obj,value){new("B01", a=value at a, b=value at b)})
>>
>> # now two "+" methods  for B00 and B01
>> setMethod("+", signature=c("B00","B00"), function(e1,e2){e1 at a+e2@a})
>> setMethod("+", signature=c("B01","B01"), function(e1,e2){e1 at b+e2@b})
>>
>> x1=new("B02", a=1, d=2)
>> x2=new("B02", a=1, d=3)
>>
>> x1+x2 ## 2 --- why?
>>    
>>
>
>My impression from reading over the man page for setIs, is that it
>isn't intended to be used to override the existing inheritance
>hierarchy.  It also mentions that the return value is the extension
>info as a list, so that could also be useful in understanding what
>setIs is doing.  Here's the output for your example:
>
>    Slots:
>                          
>    Name:        a       d
>    Class: numeric numeric
>    
>    Extends: 
>    Class "B00", directly
>    Class "B01", directly, with explicit coerce
>
>Use the contains arg of setClass to define the superclasses.  With the
>contains arg, the order determines the precedence for method lookup.
>But I suspect you know that already.  
>  
>
Yes, I have been aware of this, thank you.

>> Is there a possibility to force usage of the B01 method /without/
>> explicitely coercing x1,x2 to B01, i.e. interfere in the dispatching 
>> precedence, telling R somehow  (by particular arguments for setIs ?)  
>> to always use the is-relation defined by setIs first before mounting 
>> the hierarchy tree?
>>    
>>
> Perhaps explaining a bit more about what you are trying to accomplish
> will allow someone to provide a more helpful suggestion than mine :-)

In the "real" context, B00 stands for a class "AbscontDistribution",
which implements absolutely continuous (a.c.) distributions. B01 is
class "Gammad" which implements Gamma distributions, and B02 is
class "Exp" which implements exponential distributions. The method
still is "+", but interpreted as convolution.

For  a.c. distributions, the default method is an FFT-based numerical
convolution algorithm, while for Gamma distributions (with the same
 scale parameter), analytic, hence much more accurate convolution
formulas are used. For "Exp", I would tell R that it also 'is' a "Gammad"
distribution by a call to setIs and use the "Gammad"-method.

Of course, I could also declare explicitly "+" methods for signatures
c("Exp", "Exp"), c("Exp", "Gammad"), and c("Gammad", "Exp")  in
which I would then use as(.) to coerce "Exp" to "Gammad"
(and again the same procedure for further Gamma-methods). 

But, this would create an extra (3 or possibly much more) methods
to dispatch, and I doubt whether this really is the preferred
solution.

> If you know the inheritance structure you want before run-time, then
> I'm not seeing why you wouldn't just use the contains arg

I do not want to use the "+"  method for "B00" for accuracy reasons
(see above).

The reason why I do not want to implement "B01" ("Gammad")
as mother class of "B02" is that

(a) the slot structure is not identical --- in the real context Gamma
and Exp use different parametrizations ---
 + rate for "Exp" (cf ?rexp) and
 + shape for "Gammad" (cf rgamma)

(b) also class "Weibull" could be used as mother class to "Exp",
and I do not want to decide whether the Weibull or the
Gamma is the (more) "legitimate" mother to Exp ;-) 

I know: 'contains' could be a vector of classes ---
c("Gammad", "Weibull")  --- but then which would be
the  correct slot structure for "Exp" the one of "Gammad"
or the one of "Weibull" ?
My context is a bad example, "Gammad", "Weibull"
do have the same slots, but more generally this /is/ an issue...
 
--- So my guess was to rather implement two 'is'-relations
( "Exp" 'is' "Gammad"  and  "Exp" 'is' "Weibull")
declared by 'setIs' , and then on run time let the
dispatching mechanism decide whether to use
a Gamma or a Weibull method.

But maybe there is a better solution ?
Any suggestions are welcome.

> And if you want to force certain behavior at run-time, then I don't
> see what's wrong with an explicit coercion using as(foo, "bar").

If you have two objects E1, E2 of class "Exp" (with the same rate)
you (or the user for whom we provide these classes)  rather want to
call "+" by     E1 + E2      than
by      as(E1, "Gammad") + as(E2,"Gammad")
...

Anyway, thank you for your help

Peter



[Rd] setIs and method dispatch in S4 classes
Peter Ruckdeschel