Entering edit mode
Stephanie M. Gogarten
▴
870
@stephanie-m-gogarten-5121
Last seen 4 months ago
University of Washington
Maybe slightly off-topic, but I wanted to come to the defense of class
unions. I like them for implementing an "interface" as in Java. If a
class looks like a duck and quacks like a duck, it is a duck, where
"Duck" is a class union.
We might have many types of ducks:
setClass("Mallard", contains="Animal")
setClass("WoodDuck", contains="Animal")
setClass("RubberDuck", contains="Toy")
Not all of these ducks inherit from the same parent class, but they
have
in common that they all implement the generics lookDuck() and
quackDuck().
Then we can define
setClassUnion("Duck", c("Mallard", "WoodDuck", "RubberDuck"))
and go on to add classes and methods involving "Duck" confident that
they can all lookDuck() and quackDuck().
GWASTools is entirely based on class unions - the functions that do
the
work don't care what format the data is stored in, as long as they
implement some standard methods for retrieving the data. This has
turned out to be incredibly useful, as I've added support for
additional
storage types by defining a new class and then adding that class to
the
class union.
Stephanie
On 1/30/13 10:45 PM, Martin Morgan wrote:
> On 01/30/2013 06:55 PM, Steve Lianoglou wrote:
>> Hi Pete,
>>
>> On Wed, Jan 30, 2013 at 7:17 PM, <hickey at="" wehi.edu.au=""> wrote:
>>> Hi Steve,
>>>
>>> Thanks for your explanation. I'm just learning about the S4 class
and
>>> methods so I suspected I'd missed something. I ran your example on
my
>>> machine and it returned the same output.
>>>
>>> I've now found the real problem in my code but don't understand
why is
>>> causing inheritance problems for Rle. Basically, there's a line in
my
>>> class
>>> definitions to define a class union, namely:
>>> setClassUnion('vectorOrNULL',
>>> c("vector", "NULL"). Depending on whether that line is included
>>> before I try
>>> to construct the GRanges object determines whether the object is
>>> successfully created. Can anyone please explain this to me?
>> [snip]
>>
>>> ## But this version does not work as intended
>>> ## Firstly, start a fresh R session
>>>> library(GenomicRanges)
>> [snip]
>>
>>>> setClassUnion("vectorOrNULL", c("vector", "NULL")) ## This line
is the
>>>> culprit
>>>> out <- list(chr = rep('chr21', 10), 1:10, start = 1:10, end =
2:11)
>>>> showMethods('Rle')
>>> Function: Rle (package IRanges)
>>> values="missing", lengths="missing"
>>> values="vectorORfactor", lengths="integer"
>>> values="vectorORfactor", lengths="missing"
>>> values="vectorORfactor", lengths="numeric"
>>>
>>>> gr <- GRanges(seqnames = out[['chr']], ranges = IRanges(start =
>>>> out[['start']], end = out[['end']]))
>>> Error in function (classes, fdef, mtable) :
>>> unable to find an inherited method for function ?Rle? for
signature
>>> ?"character", "missing"?
>>>> showMethods('Rle')
>>> Function: Rle (package IRanges)
>>> values="missing", lengths="missing"
>>> values="vectorORfactor", lengths="integer"
>>> values="vectorORfactor", lengths="missing"
>>> values="vectorORfactor", lengths="numeric"
>>>
>>> ## Inheritance problems for Rle
>>
>> Interesting ... my guess is because with your new class union, both
of
>> these are now TRUE:
>>
>> R> is(c('a', 'b', 'c'), 'vectorORfactor')
>> [1] TRUE
>>
>> R> is(c('a', 'b', 'c'), 'vectorOrNULL')
>> [1] TRUE
>>
>> But it really feels like the class union shouldn't be getting in
the
>> way -- I mean, if one then writes an Rle method for
c("vectorOrNULL",
>> "missing"), I can imagine what the problem might be, but that's not
>> the case here.
>>
>> Hmmm ... if I were a bit bolder, I'd hazard that this might even be
a
>> bug somewhere in some S4 dispatching mojo, but I'm not
>> well-versed-enough in its voodoo to make that claim.
>>
>> I suspect Martin will likely chime in to point out what is the
what,
>> here ;-)
>
> Yep, this is a puzzler. Here's what happens in a fresh R session:
>
> > setClassUnion("vectorORfactor", c("vector", "factor"))
> > getClass("numeric")
> Class "numeric" [package "methods"]
>
> No Slots, prototype of class "numeric"
>
> Extends:
> Class "vector", directly
> Class "vectorORfactor", by class "vector", distance 2
>
> Known Subclasses:
> Class "integer", directly
> Class "ordered", by class "factor", distance 3
>
> and then
>
> > setClassUnion("vectorOrNULL", c("vector", "NULL"))
> > getClass("numeric")
> Class "numeric" [package "methods"]
>
> No Slots, prototype of class "numeric"
>
> Extends:
> Class "vector", directly
> Class "vectorORfactor", by class "vector", distance 2
> Class "vectorOrNULL", by class "vector", distance 2
>
> Known Subclasses:
> Class "integer", directly
> Class "ordered", by class "factor", distance 3
>
> Notice that "numeric" extends our two class unions.
>
> Now when we're dealing with a package, focusing on the 'Extends:'
component
>
> library(IRanges)
> > getClass("numeric")
> ...
> Extends:
> Class "vector", directly
> Class "atomic", directly
> Class "vectorORfactor", by class "vector", distance 2
> > setClassUnion("vectorOrNULL", c("vector", "NULL"))
> > getClass("numeric")
> ...
> Extends:
> Class "vector", directly
> Class "vectorOrNULL", by class "vector", distance 2
>
> so we have replaced rather than amended Extends:. I think the error
with
> method dispatch follows from this -- we end up looking for a method
> defined on vectorORNULL, and don't find one.
>
> I think the problem is in methods::assignClassDef, but things get a
bit
> hairy for me; maybe there are class definitions for numeric that are
> found in IRanges, and in methods, and the latter over-writes the
former?
>
> A work-around seems to be to setClassUnion() before loading IRanges.
>
> I find class unions pretty weird -- reach in to the class hierarchy
and
> saying no, inheritance works _this_ way and at the same time making
> things complicated for ourselves because we always have to check
whether
> the slot is a vector or NULL -- I wonder what you're hoping to
> accomplish with this? I know the pattern is well-established in
IRanges...
>
> Martin
>
>>
>> -steve
>>
>
>