[Rd] unary class union of an S3 class

Michael Lawrence lawrence.michael at gene.com
Sat Mar 19 09:22:25 CET 2016


On Sat, Mar 19, 2016 at 12:10 AM, Hervé Pagès <hpages at fredhutch.org> wrote:

> On 03/18/2016 03:28 PM, Michael Lawrence wrote:
>
>>
>> On Fri, Mar 18, 2016 at 2:53 PM, Hervé Pagès <hpages at fredhutch.org
>> <mailto:hpages at fredhutch.org>> wrote:
>>
>>     Hi,
>>
>>     Short story
>>     -----------
>>
>>        setClassUnion("ArrayLike", "array")
>>
>>        showClass("ArrayLike")  # no slot
>>
>>        setClass("MyArrayLikeConcreteSubclass",
>>            contains="ArrayLike",
>>            representation(stuff="ANY")
>>        )
>>
>>        showClass("MyArrayLikeConcreteSubclass") # 2 slots!!
>>
>>     That doesn't seem right.
>>
>>     Long story
>>     ----------
>>
>>     S4 provides at least 3 ways to create a little class hierarchy
>>     like this:
>>
>>             FooLike ............. virtual class with no slot
>>              ^   ^
>>              |   |
>>            foo   anotherfoo ..... 2 concrete subclasses
>>
>>     (1) The "standard" way: define FooLike first, then foo and anotherfoo
>>     as subclasses of FooLike:
>>
>>        setClass("FooLike")
>>
>>        setClass("foo",
>>            contains="FooLike",
>>            representation(stuff="ANY")
>>        )
>>
>>        setClass("anotherfoo",
>>            contains="FooLike",
>>            representation(stuff="ANY")
>>        )
>>
>>        showClass("FooLike")    # displays foo and anotherfoo as
>>                                # known subclasses
>>
>>        x1 <- new("foo")
>>        is(x1, "foo")           # TRUE
>>        is(x1, "FooLike")       # TRUE
>>        is(x1, "anotherfoo")    # FALSE
>>
>>        x2 <- new("anotherfoo")
>>        is(x2, "anotherfoo")    # TRUE
>>        is(x2, "FooLike")       # TRUE
>>        is(x2, "foo")           # FALSE
>>
>>     Everything works as expected.
>>
>>     (2) Using a class union: define foo and anotherfoo first, then FooLike
>>     as the union of foo and anotherfoo:
>>
>>        setClass("foo", representation(stuff="ANY"))
>>        setClass("anotherfoo", representation(stuff="ANY"))
>>        setClassUnion("FooLike", c("foo", "anotherfoo"))
>>
>>        showClass("FooLike")    # displays foo and anotherfoo as
>>                                # known subclasses
>>
>>     (3) Using a *unary* class union: define foo first, then FooLike as the
>>     (unary) union of foo, then anotherfoo as a subclass of FooLike:
>>
>>        setClass("foo", representation(stuff="ANY"))
>>        setClassUnion("FooLike", "foo")
>>
>>        showClass("FooLike")   # displays foo as the only known subclass
>>
>>        setClass("anotherfoo",
>>            contains="FooLike",
>>            representation(stuff="ANY")
>>        )
>>
>>        showClass("FooLike")   # now displays foo and anotherfoo as
>>                               # known subclasses
>>
>>     The 3 ways lead to the same hierarchy. However the 3rd way is
>>     interesting because it allows one to define the FooLike virtual
>>     class as the parent of an existing foo class that s/he doesn't
>>     control.
>>
>>
>> Why not use setIs() for this?
>>
>
>   > setClass("ArrayLike")
>   > setIs("array", "ArrayLike")
>   Error in setIs("array", "ArrayLike") :
>     class “array” is sealed; new superclasses can not be defined, except
> by 'setClassUnion'
>
> How do you define a virtual class as the parent of an existing class
> with setIs?
>
>
You can only do that with setClassUnion(). But the new classes should use
setIs() to inherit from the union. So it's:

setClassUnion("ArrayLike", "array")
setClass("MyArrayLike")
setIs("MyArrayLike", "ArrayLike")


> Everything then behaves as expected. I
>> don't think it makes much sense to "contain" a class union.
>>
>
> Why is that? A class union is just a virtual class with no slot
> that is the parent of the classes that are in the union. All the
> classes in the union contain their parent. What's interesting is that
> this union is actually open to new members: when I later define a new
> class that contains the class union, I'm just adding a new member to
> the union.
>
> Rather, you
>> just want to establish the inheritance relationship.
>>
>
> Isn't what I'm doing when I define a new class that contains the
> class union?
>

Containing does two things: establishes the is() relationship and adds
slots to the class. These slots are comprised of the slots of the contained
class, and as a special case the "array" class and other native types
confer a data part that comes from the prototype of the class. The "array"
class has a double vector with a dim attribute as its prototype. That is
all well understood. What is surprising is that "ArrayLike" has the same
prototype as "array". That happens via setIs(doComplete=TRUE), called by
setClassUnion(). When a class gains its first non-virtual child, the parent
assumes the prototype of its child.  I'm not sure why, but the logic is
very explicit and I've come to just accept it as a "feature". I ran into
this some months ago when defining my own ArrayLike when working on a very
similar package to the one you are developing ;)


>
>>     For example, to define an ArrayLike class:
>>
>>        setClassUnion("ArrayLike", "array")
>>        showClass("ArrayLike")  # displays array as a known subclass
>>
>>     Note that ArrayLike is virtual with no slots (analog to a Java
>>     Interface), which is what is expected.
>>
>>        setClass("MyArrayLikeConcreteSubclass",
>>            contains="ArrayLike",
>>            representation(stuff="ANY")
>>        )
>>
>>        showClass("MyArrayLikeConcreteSubclass")  # shows 2 slots!!
>>
>>     What is the .Data slot doing here? I would expect to see that slot
>>     if MyArrayLikeConcreteSubclass was extending array but this is not
>>     the case here.
>>
>>        a <- new("MyArrayLikeConcreteSubclass")
>>
>>        is(a, "MyArrayLikeConcreteSubclass")  # TRUE  --> ok
>>        is(a, "ArrayLike")                    # TRUE  --> ok
>>        is(a, "array")                        # FALSE --> ok
>>
>>     But:
>>
>>        is.array(a)  # TRUE --> not ok!
>>
>>     Is is.array() confused by the presence of the .Data slot?
>>
>>
>> It looks like the unary union somehow equates ArrayLike and array
>>
>
> Clearly the unary union makes ArrayLike a parent of array, as it should
> be. This can be confirmed by extends():
>
>   > extends("array", "ArrayLike")
>   [1] TRUE
>   > extends("ArrayLike", "array")
>   [1] FALSE
>
> The results for is(a, "ArrayLike") (TRUE) and is(a, "array") (FALSE)
> on a MyArrayLikeConcreteSubclass instance are consistent with this.
>
> So the little 3-class hierarchy I end up with in the above example
> is exactly how expected:
>
>          ArrayLike
>           ^    ^
>           |    |
>       array    MyArrayLikeConcreteSubclass
>
> What is not expected is that MyArrayLikeConcreteSubclass has a .Data
> slot and that is.array(a) returns TRUE on a MyArrayLikeConcreteSubclass
> object.
>
> H.
>
> and
>> thus makes ArrayLike confer a dim attribute (and thus is.array(a)
>> returns TRUE). Since S4 objects cannot have attributes that are not
>> slots, it must do this via a data part, thus the .Data slot.
>>
>>     I can fix it by defining an "is.array" method for
>>     MyArrayLikeConcreteSubclass objects:
>>
>>        setMethod("is.array", "MyArrayLikeConcreteSubclass",
>>            function(x) FALSE
>>        )
>>
>>     However, it feels that I shouldn't have to do this.
>>
>>     Is the presence of the .Data slot in MyArrayLikeConcreteSubclass
>>     objects an unintended feature?
>>
>>     Thanks,
>>     H.
>>
>>      > sessionInfo()
>>     R Under development (unstable) (2016-01-07 r69884)
>>     Platform: x86_64-pc-linux-gnu (64-bit)
>>     Running under: Ubuntu 14.04.4 LTS
>>
>>     locale:
>>       [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
>>       [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8
>>       [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8
>>       [7] LC_PAPER=en_US.UTF-8       LC_NAME=C
>>       [9] LC_ADDRESS=C               LC_TELEPHONE=C
>>     [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
>>
>>     attached base packages:
>>     [1] stats     graphics  grDevices utils     datasets  methods   base
>>
>>     --
>>     Hervé Pagès
>>
>>     Program in Computational Biology
>>     Division of Public Health Sciences
>>     Fred Hutchinson Cancer Research Center
>>     1100 Fairview Ave. N, M1-B514
>>     P.O. Box 19024
>>     Seattle, WA 98109-1024
>>
>>     E-mail: hpages at fredhutch.org <mailto:hpages at fredhutch.org>
>>     Phone: (206) 667-5791 <tel:%28206%29%20667-5791>
>>     Fax: (206) 667-1319 <tel:%28206%29%20667-1319>
>>
>>     ______________________________________________
>>     R-devel at r-project.org <mailto:R-devel at r-project.org> mailing list
>>     https://stat.ethz.ch/mailman/listinfo/r-devel
>>
>>
>>
> --
> Hervé Pagès
>
> Program in Computational Biology
> Division of Public Health Sciences
> Fred Hutchinson Cancer Research Center
> 1100 Fairview Ave. N, M1-B514
> P.O. Box 19024
> Seattle, WA 98109-1024
>
> E-mail: hpages at fredhutch.org
> Phone:  (206) 667-5791
> Fax:    (206) 667-1319
>
>

	[[alternative HTML version deleted]]



More information about the R-devel mailing list