

Dear all,
I have a question regarding setIs and method dispatch in S4 classes:
Consider the following example:
#####################################################
## A02 "contains" A01 by setIs:
setClass("A01", representation(a="numeric",b="numeric"))
setClass("A02", representation(a="numeric",d="numeric"))
setIs("A02","A01",coerce=function(obj){new("A01", a=obj@a, b=obj@d)},
replace=function(obj,value){new("A01", a=value@a, b=value@b)})
#only a "+" method for A01
setMethod("+", signature=c("A01","A01"), function(e1,e2){e1@b+e2@b})
x1=new("A02", a=1, d=2)
x2=new("A02", a=1, d=3)
x1+x2 ## 5 as expected
## 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@a, b=obj@d)},
replace=function(obj,value){new("B01", a=value@a, b=value@b)})
# now two "+" methods for B00 and B01
setMethod("+", signature=c("B00","B00"), function(e1,e2){e1@a+e2@a})
setMethod("+", signature=c("B01","B01"), function(e1,e2){e1@b+e2@b})
x1=new("B02", a=1, d=2)
x2=new("B02", a=1, d=3)
x1+x2 ## 2  why?
#####################################################
(tested on 2.3.0 alpha (20060405 r37653))
I would have guessed that this again would give 5, as B02 is deeper
in the class hierarchy than B00 and the calls to setIs specifies an
unconditional
isrelation.
What is the principle then behind this dispatching mechanism?
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 isrelation defined by setIs first before mounting
the hierarchy tree?
Thank you for your attention
Peter
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/rdevel


as a fan of S4 i had a look at this; more definitive
replies are undoubtedly to come
> ## 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")
the direct specification of containment in the next statement
is important for the dispatch process
> setClass("B02", representation(a="numeric",d="numeric"), contains= "B00")
if we omit the containment assertion your expectations are met
setClass("B02", representation(a="numeric",d="numeric"))
setIs("B02","B01",coerce=function(obj){new("B01", a=obj@a, b=obj@d)},
replace=function(obj,value){new("B01", a=value@a, b=value@b)})
two diagnostic steps that are illuminating are
getClass("B02") under both approaches (with and without contains="B00"
in definition of B02 class)
and
getMethods("+") under both approaches
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/rdevel


Hi Peter,
Peter Ruckdeschel < [hidden email]> 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@a, b=obj@d)},
> replace=function(obj,value){new("B01", a=value@a, b=value@b)})
>
> # now two "+" methods for B00 and B01
> setMethod("+", signature=c("B00","B00"), function(e1,e2){e1@a+e2@a})
> setMethod("+", signature=c("B01","B01"), function(e1,e2){e1@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.
> 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 isrelation 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 :)
If you know the inheritance structure you want before runtime, then
I'm not seeing why you wouldn't just use the contains arg.
And if you want to force certain behavior at runtime, then I don't
see what's wrong with an explicit coercion using as(foo, "bar").
But perhaps I'm missing something.
Cheers,
+ seth
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/rdevel


Hi Seth ,
thank you for your reply.
Seth Falcon < [hidden email]> writes:
>Peter Ruckdeschel < [hidden email]> 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@a, b=obj@d)},
>> replace=function(obj,value){new("B01", a=value@a, b=value@b)})
>>
>> # now two "+" methods for B00 and B01
>> setMethod("+", signature=c("B00","B00"), function(e1,e2){e1@a+e2@a})
>> setMethod("+", signature=c("B01","B01"), function(e1,e2){e1@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 isrelation 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 FFTbased 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 Gammamethods).
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 runtime, 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 runtime, 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
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/rdevel


From your description of the application, it sounds like you would be
better off just forcing "+" to behave as you want. Using inheritance is
a much more powerful mechanism & can introduce results you don't want,
as it seems to have in this case.
An important point about using inheritance is that the subclass is a
asserted to be substitutable for the superclass for ALL purposes. This
applies whether using "contains=" or setIs().
When the focus is on a particular function, it's usually better to
implement methods for that function, maybe along with setAs()
methodsnot setIs().
It seems likely that such a solution would be cleaner in design, not to
mention that it would likely work. (see also suggestion below)
Peter Ruckdeschel wrote:
>Hi Seth ,
>
>thank you for your reply.
>
>Seth Falcon < [hidden email]> writes:
>
>
>
>>Peter Ruckdeschel < [hidden email]> 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@a, b=obj@d)},
>>> replace=function(obj,value){new("B01", a=value@a, b=value@b)})
>>>
>>># now two "+" methods for B00 and B01
>>>setMethod("+", signature=c("B00","B00"), function(e1,e2){e1@a+e2@a})
>>>setMethod("+", signature=c("B01","B01"), function(e1,e2){e1@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 isrelation 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 FFTbased 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 Gammamethods).
>
>But, this would create an extra (3 or possibly much more) methods
>to dispatch, and I doubt whether this really is the preferred
>solution.
>
>
Why not? And you can avoid some of the extra methods by defining a
virtual class that is the union of the classes for which you want the
new methods.
Something like (untested code!)
setClassUnion("analyticConvolution", c("Exp", "Gammad"))
setMethod("+", c("analyticConvolution", "analyticConvolution"), ....)
>
>
>>If you know the inheritance structure you want before runtime, 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 runtime, 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
>
>______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/rdevel>
>
>
[[alternative HTML version deleted]]
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/rdevel


Hi John,
I found your comments helpful, even though this isn't _my_ question.
But now I have one of my own :)
John Chambers < [hidden email]> writes:
>>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 Gammamethods).
>>
>>But, this would create an extra (3 or possibly much more) methods
>>to dispatch, and I doubt whether this really is the preferred
>>solution.
>>
>>
> Why not? And you can avoid some of the extra methods by defining a
> virtual class that is the union of the classes for which you want the
> new methods.
>
> Something like (untested code!)
>
> setClassUnion("analyticConvolution", c("Exp", "Gammad"))
> setMethod("+", c("analyticConvolution", "analyticConvolution"),
> ....)
Why class union here and not an abstract superclass?
If you "own" the Exp and Gammad classes, would an abstract superclass
work as well? I think so.
However, if you don't own the Exp and Gammad classes, I can see that
the class union approach allows you the flexibility of defining a
superclass posthoc.
I guess I have the sense that class unions are fancy/tricky (a number
of popular languages don't have that concept, AFAIK). That isn't a
reason not to use them in a langauge that does support them, of
course.
It is an interesting design question. On the one hand, one could
argue for abstract superclasses when possible because they are "less
tricky" (and you need them when you want to share slots). On the
other hand, the class union approach provides a more loosely coupled
design since members of the union don't have to know about each other.
Hmm, I think I understand class unions a lot better already. Thanks.
If I'm terribly offtrack, please let me know.
+ seth
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/rdevel


Seth Falcon wrote:
>Hi John,
>
>I found your comments helpful, even though this isn't _my_ question.
>But now I have one of my own :)
>
>John Chambers < [hidden email]> writes:
>
>
>>>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 Gammamethods).
>>>
>>>But, this would create an extra (3 or possibly much more) methods
>>>to dispatch, and I doubt whether this really is the preferred
>>>solution.
>>>
>>>
>>>
>>>
>>Why not? And you can avoid some of the extra methods by defining a
>>virtual class that is the union of the classes for which you want the
>>new methods.
>>
>>Something like (untested code!)
>>
>>setClassUnion("analyticConvolution", c("Exp", "Gammad"))
>>setMethod("+", c("analyticConvolution", "analyticConvolution"),
>>....)
>>
>>
>
>Why class union here and not an abstract superclass?
>
>If you "own" the Exp and Gammad classes, would an abstract superclass
>work as well? I think so.
>
>
Yes, as is said frequently of a certain other language "There's more
than one way to do it"
My own feeling is that class unions are a convenient shorthand & clearer
than explicitly defining the superclass and then having to establish the
inheritance separately for the two subclasses. Although the
documentation mentions that they _must_ be used for classes you don't
own, that's not their only purpose.
Virtual classes (ahem, I assume that's what you meant by "abstract"
;)) may or may not have slots of their own. Creating a virtual class
"analyticConvolution" and doing two setIs() calls would in fact be
roughly equivalent to the setClassUnion, but not as clear, IMO.
If the superclass was really crucial to the model, that would make it
more natural to have it explicitly in the contains= for the individual
subclasses. Here, though, it seems more like a computational
convenience for a fairly small part of the overall package, so isolating
it in a single setClassUnion() call seems more natural.
Obviously, a question of taste and style.
>However, if you don't own the Exp and Gammad classes, I can see that
>the class union approach allows you the flexibility of defining a
>superclass posthoc.
>
>I guess I have the sense that class unions are fancy/tricky (a number
>of popular languages don't have that concept, AFAIK). That isn't a
>reason not to use them in a langauge that does support them, of
>course.
>
>It is an interesting design question. On the one hand, one could
>argue for abstract superclasses when possible because they are "less
>tricky" (and you need them when you want to share slots). On the
>other hand, the class union approach provides a more loosely coupled
>design since members of the union don't have to know about each other.
>
>Hmm, I think I understand class unions a lot better already. Thanks.
>If I'm terribly offtrack, please let me know.
>
>+ seth
>
>______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/rdevel>
>
>
[[alternative HTML version deleted]]
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/rdevel


Hi Seth and John,
Thank you for your helpful responses,
>John Chambers < [hidden email]> writes:
>>From your description of the application, it sounds like you would be
>>better off just forcing "+" to behave as you want. Using inheritance is
>>a much more powerful mechanism & can introduce results you don't want,
>>as it seems to have in this case.
>>
>>An important point about using inheritance is that the subclass is a
>>asserted to be substitutable for the superclass for ALL purposes. This
>>applies whether using "contains=" or setIs().
I am not sure whether I got the meaning of "substitutable for the
superclass for ALL purposes" :
In the application I sketched, any Exp(rate = lambda) distribution
really /is/ a Gammad(shape = 1, scale = 1/lambda) distribution;
so my understanding is that "Exp" is substitutable for "Gammad"
for ALL purposes.
"Gammad" was not designed to be the motherclass to "Exp" right
from the beginning because the same 'is'relation also applies to
"Weibull": any Exp(rate = lambda) distribution /is/ a
Weibull(shape = 1, scale = 1/lambda) distribution.
Does "substitutable for the superclass for ALL purposes"
mean 'without ambiguity' (as might enter through Weibull/Gammad)?
>>When the focus is on a particular function, it's usually better to
>>implement methods for that function, maybe along with setAs()
>>methodsnot setIs().
You mean I should not leave the coercion decision up to the dispatching
mechanism?
>>It seems likely that such a solution would be cleaner in design, not to
>>mention that it would likely work. (see also suggestion below)
Yes, your indication does work; thank you!
>>Peter Ruckdeschel < [hidden email]> writes:
>>>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 Gammamethods).
>>>
>>>But, this would create an extra (3 or possibly much more) methods
>>>to dispatch, and I doubt whether this really is the preferred
>>>solution.
>>>
>> Why not?
It simply did not seem to me elegant to have three calls to
setMethod() doing more or less the same thing.
I thought that, as elegant as R solutions from the R core are
most times, there should be some mechanism to avoid this
threefold codeand in fact you indicated how to
thank you!
>> And you can avoid some of the extra methods by defining a
>> virtual class that is the union of the classes for which you
>> want the new methods.
>>
>> Something like (untested code!)
>>
>> setClassUnion("analyticConvolution", c("Exp", "Gammad"))
>> setMethod("+", c("analyticConvolution", "analyticConvolution"),
>> ....)
Seth Falcon < [hidden email]> writes:
> Why class union here and not an abstract superclass?
Am I right: the class generated by setClassUnion() does not
enter the inheritance tree / mechanism?
setClassUnion()at least in my casesolves the problem;
thank you again.
[snip]
Peter
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/rdevel

