[Rd] unary class union of an S3 class
Michael Lawrence
lawrence.michael at gene.com
Sat Mar 19 14:35:26 CET 2016
On Sat, Mar 19, 2016 at 4:29 AM, Hervé Pagès <hpages at fredhutch.org> wrote:
> On 03/19/2016 01:22 AM, Michael Lawrence wrote:
>
>>
>>
>> On Sat, Mar 19, 2016 at 12:10 AM, Hervé Pagès <hpages at fredhutch.org
>> <mailto: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>
>> <mailto: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.
>>
>
> I understand that. But in that case, since a class union has no slots,
> one would expect that using setIs() is equivalent to containing.
>
> 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".
>>
>
> Never noticed that. Thanks for clarifying. So with this "feature":
>
> - setClassUnion("A", c("B", "C")) is not the same as
> setClassUnion("A", c("C", "B"))
>
> - if 2 packages define concrete subclasses of a virtual
> class defined in a 3rd package, the prototype of the virtual
> class will depend on the order the packages are loaded
>
> - using setIs("MyArrayLike", "ArrayLike") is not equivalent
> to containing (even though ArrayLike has no slots)
>
> - containing adds an undesirable .Data slot
>
> - containing breaks is.array() but not is( , "array")
>
> Seems pretty harmful to me. Would be good to understand the rationale
> behind this feature. In particular it's not clear to me why a virtual
> class with no slot would need to have a prototype at all (i.e. other
> than NULL).
>
> 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 ;)
>>
>
> After giving it more thoughts I realized that I can do without the
> ArrayLike class. That will keep the class hierarchy in HDF5Array to the
> strict minimum.
>
>
Yea I've come to realize that declaring virtual classes that indicate
whether an object behaves like a base type is overkill. It usually suffices
to say that the object satisfies the basic contract of an array, list,
vector, etc. It would be nice to have something like a Java interface for
specifying such contracts.
> Thanks for the feedback,
> H.
>
>
>>
>>
>> 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>
>> <mailto:hpages at fredhutch.org <mailto:hpages at fredhutch.org>>
>> Phone: (206) 667-5791 <tel:%28206%29%20667-5791>
>> <tel:%28206%29%20667-5791>
>> Fax: (206) 667-1319 <tel:%28206%29%20667-1319>
>> <tel:%28206%29%20667-1319>
>>
>> ______________________________________________
>> R-devel at r-project.org <mailto:R-devel at r-project.org>
>> <mailto: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 <mailto:hpages at fredhutch.org>
>> Phone: (206) 667-5791 <tel:%28206%29%20667-5791>
>> Fax: (206) 667-1319 <tel:%28206%29%20667-1319>
>>
>>
>>
> --
> 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