[Rd] unary class union of an S3 class
Hervé Pagès
hpages at fredhutch.org
Sat Mar 19 12:29:36 CET 2016
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.
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
More information about the R-devel
mailing list