Blätte, Andreas
2020-Oct-05 20:47 UTC
[Rd] S4 - inheritance changed by order of setClassUnion and setAs()
Dear colleagues, there is a behaviour with S4 (virtual) classes that I find very hard to understand: Depending on the position of setAs(), the tree of inheritance changes. This is my baseline example that defines the classes "grandma", "mother", "daughter" and a virtual class "mr_x". For a new instance if "daughter", "mr_x" is betweeen "mother" and "grandma". setClass("grandma", slots = c(a = "character")) setClass("mother", slots = c(b = "matrix"), contains = "grandma") setClass("daughter", slots = c(c = "list"), contains = "mother") setClassUnion(name = "mr_x", members = c("daughter", "mother")) setAs(from = "daughter", to = "grandma", def = function(from) new("grandma")) is(new("daughter")) [1] "daughter" "mother" "mr_x" "grandma" Yet if I change the order of setAs() and setClassUnion(), this alters the pattern of inheritance. setClass("grandma", slots = c(a = "character")) setClass("mother", slots = c(b = "matrix"), contains = "grandma") setClass("daughter", slots = c(c = "list"), contains = "mother") setAs(from = "daughter", to = "grandma", def = function(from) new("grandma")) setClassUnion(name = "mr_x", members = c("daughter", "mother")) is(new("daughter")) [1] "daughter" "mother" "grandma" "mr_x" Is there a reasonable explanation for this behavior? I could not find any and I would appreciate your help. If it is not an unintended behavior, I find it very confusing and hard to anticipate. Kind regads Andreas -- Prof. Dr. Andreas Bl?tte Professor of Public Policy and Regional Politics University of Duisburg-Essen [[alternative HTML version deleted]]
Gabriel Becker
2020-Oct-06 00:02 UTC
[Rd] S4 - inheritance changed by order of setClassUnion and setAs()
Andreas, As far as I can tell (/conjecture), this is because the list of classes a particular class inherits from directly is appended to as needed, and so the order that a class extends others isd refined by the order that those connections are defined. We can see this with two setClassUnion calls, rather than required setAs:> setClass("grandma", slots = c(a = "character"))> setClass("mother", slots = c(b = "matrix"), contains = "grandma")> setClass("daughter", slots = c(c = "list"), contains = "mother")> setClassUnion(name = "mr_x", members = c("daughter", "mother"))> setClassUnion(name = "mr_y", members = c("daughter", "mother"))> getClass("daughter")Class "daughter" [in ".GlobalEnv"] Slots: Name: c b a Class: list matrix character Extends: Class "mother", directly Class "mr_x", directly Class "mr_y", directly Class "grandma", by class "mother", distance 2> setClass("grandma2", slots = c(a = "character"))> setClass("mother2", slots = c(b = "matrix"), contains = "grandma2")> setClass("daughter2", slots = c(c = "list"), contains = "mother2")> setClassUnion(name = "mr_y2", members = c("daughter2", "mother2"))> setClassUnion(name = "mr_x2", members = c("daughter2", "mother2"))> getClass("daughter2")Class "daughter2" [in ".GlobalEnv"] Slots: Name: c b a Class: list matrix character Extends: Class "mother2", directly Class "mr_y2", directly Class "mr_x2", directly Class "grandma2", by class "mother2", distance 2 Note that mr_y2 appears in the list before mr_x2 the second block. The same thing is happening with setAs which (somewhat contrary to my expectations, admittedly) causes extends to consider "daughter" to inherit *directly* from "grandma" in your example (though it does note its via explicit coercion). I think the take-away here is that when modifying the class inheritance structure explicitly, via setClassUnion or setAs (or, I assume, setIs) order inherently matters. In fact order also matters for multiple inheritence via the normal contains mechanism. In practice, how could it not matter? Multiple inheritence is very powerful but dangerous.> setClass("person1", slots = c(f = "character"))> setClass("person2", slots = c(g = "character"))> setClass("people1",* contains = c("person1", "person2")*)> getClass("people1")Class "people1" [in ".GlobalEnv"] Slots: Name: f g Class: character character Extends: "person1", "person2"> setClass("people2", *contains = c("person2", "person1")*)> getClass("people2")Class "people2" [in ".GlobalEnv"] Slots: Name: g f Class: character character Extends: "person2", "person1"> setGeneric("ohno", function(obj) standardGeneric("ohno")+ ) [1] "ohno"> setMethod("ohno", "person1", function(obj) "person1!")> setMethod("ohno", "person2", function(obj) "person2! Oh No!")*> ohno(new("people1"))* *[1] "person1!"* *> ohno(new("people2"))* *[1] "person2! Oh No!"* Not sure if that helps any or not, but thats what I see here. And again, if I got anything wrong here, someone please correct me :) Best, ~G On Mon, Oct 5, 2020 at 1:47 PM Bl?tte, Andreas <andreas.blaette at uni-due.de> wrote:> Dear colleagues, > > there is a behaviour with S4 (virtual) classes that I find very hard to > understand: Depending on the position > of setAs(), the tree of inheritance changes. > > This is my baseline example that defines the classes "grandma", "mother", > "daughter" and a virtual > class "mr_x". For a new instance if "daughter", "mr_x" is betweeen > "mother" and "grandma". > > setClass("grandma", slots = c(a = "character")) > setClass("mother", slots = c(b = "matrix"), contains = "grandma") > setClass("daughter", slots = c(c = "list"), contains = "mother") > setClassUnion(name = "mr_x", members = c("daughter", "mother")) > setAs(from = "daughter", to = "grandma", def = function(from) > new("grandma")) > is(new("daughter")) > > [1] "daughter" "mother" "mr_x" "grandma" > > Yet if I change the order of setAs() and setClassUnion(), this alters the > pattern of inheritance. > > setClass("grandma", slots = c(a = "character")) > setClass("mother", slots = c(b = "matrix"), contains = "grandma") > setClass("daughter", slots = c(c = "list"), contains = "mother") > setAs(from = "daughter", to = "grandma", def = function(from) > new("grandma")) > setClassUnion(name = "mr_x", members = c("daughter", "mother")) > is(new("daughter")) > > [1] "daughter" "mother" "grandma" "mr_x" > > Is there a reasonable explanation for this behavior? I could not find any > and I would appreciate > your help. If it is not an unintended behavior, I find it very confusing > and hard to anticipate. > > Kind regads > Andreas > > -- > Prof. Dr. Andreas Bl?tte > Professor of Public Policy and Regional Politics > University of Duisburg-Essen > > [[alternative HTML version deleted]] > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >[[alternative HTML version deleted]]
Blätte, Andreas
2020-Oct-06 08:30 UTC
[Rd] S4 - inheritance changed by order of setClassUnion and setAs()
Dear Gabriel, thank you so much for the scrutiny you took to look into this. Your explanation gives me a much better sense for what may be going on. Admittedly, I still find it hard to understand why setAs() may have a side effect on inheritance. Looking at the documentation of setAs() again, this is an expected behaviour of setIs(), but not from setAs(). Maybe it makes sense to add a respective explicit explanation in the documentation of setAs()? For the time being, my personal take away is that I will try to circumvent setClassUnion() in combination with setAs(). Thanks again Andreas Von: Gabriel Becker <gabembecker at gmail.com> Datum: Dienstag, 6. Oktober 2020 um 02:02 An: Bl?tte <andreas.blaette at uni-due.de> Cc: Peter Dalgaard via R-devel <r-devel at r-project.org> Betreff: Re: [Rd] S4 - inheritance changed by order of setClassUnion and setAs() Andreas, As far as I can tell (/conjecture), this is because the list of classes a particular class inherits from directly is appended to as needed, and so the order that a class extends others isd refined by the order that those connections are defined. We can see this with two setClassUnion calls, rather than required setAs:> setClass("grandma", slots = c(a = "character"))> setClass("mother", slots = c(b = "matrix"), contains = "grandma")> setClass("daughter", slots = c(c = "list"), contains = "mother")> setClassUnion(name = "mr_x", members = c("daughter", "mother"))> setClassUnion(name = "mr_y", members = c("daughter", "mother"))> getClass("daughter")Class "daughter" [in ".GlobalEnv"] Slots: Name: c b a Class: list matrix character Extends: Class "mother", directly Class "mr_x", directly Class "mr_y", directly Class "grandma", by class "mother", distance 2> setClass("grandma2", slots = c(a = "character"))> setClass("mother2", slots = c(b = "matrix"), contains = "grandma2")> setClass("daughter2", slots = c(c = "list"), contains = "mother2")> setClassUnion(name = "mr_y2", members = c("daughter2", "mother2"))> setClassUnion(name = "mr_x2", members = c("daughter2", "mother2"))> getClass("daughter2")Class "daughter2" [in ".GlobalEnv"] Slots: Name: c b a Class: list matrix character Extends: Class "mother2", directly Class "mr_y2", directly Class "mr_x2", directly Class "grandma2", by class "mother2", distance 2 Note that mr_y2 appears in the list before mr_x2 the second block. The same thing is happening with setAs which (somewhat contrary to my expectations, admittedly) causes extends to consider "daughter" to inherit directly from "grandma" in your example (though it does note its via explicit coercion). I think the take-away here is that when modifying the class inheritance structure explicitly, via setClassUnion or setAs (or, I assume, setIs) order inherently matters. In fact order also matters for multiple inheritence via the normal contains mechanism. In practice, how could it not matter? Multiple inheritence is very powerful but dangerous.> setClass("person1", slots = c(f = "character"))> setClass("person2", slots = c(g = "character"))> setClass("people1", contains = c("person1", "person2"))> getClass("people1")Class "people1" [in ".GlobalEnv"] Slots: Name: f g Class: character character Extends: "person1", "person2"> setClass("people2", contains = c("person2", "person1"))> getClass("people2")Class "people2" [in ".GlobalEnv"] Slots: Name: g f Class: character character Extends: "person2", "person1"> setGeneric("ohno", function(obj) standardGeneric("ohno")+ ) [1] "ohno"> setMethod("ohno", "person1", function(obj) "person1!")> setMethod("ohno", "person2", function(obj) "person2! Oh No!")> ohno(new("people1"))[1] "person1!"> ohno(new("people2"))[1] "person2! Oh No!" Not sure if that helps any or not, but thats what I see here. And again, if I got anything wrong here, someone please correct me :) Best, ~G On Mon, Oct 5, 2020 at 1:47 PM Bl?tte, Andreas <andreas.blaette at uni-due.de<mailto:andreas.blaette at uni-due.de>> wrote: Dear colleagues, there is a behaviour with S4 (virtual) classes that I find very hard to understand: Depending on the position of setAs(), the tree of inheritance changes. This is my baseline example that defines the classes "grandma", "mother", "daughter" and a virtual class "mr_x". For a new instance if "daughter", "mr_x" is betweeen "mother" and "grandma". setClass("grandma", slots = c(a = "character")) setClass("mother", slots = c(b = "matrix"), contains = "grandma") setClass("daughter", slots = c(c = "list"), contains = "mother") setClassUnion(name = "mr_x", members = c("daughter", "mother")) setAs(from = "daughter", to = "grandma", def = function(from) new("grandma")) is(new("daughter")) [1] "daughter" "mother" "mr_x" "grandma" Yet if I change the order of setAs() and setClassUnion(), this alters the pattern of inheritance. setClass("grandma", slots = c(a = "character")) setClass("mother", slots = c(b = "matrix"), contains = "grandma") setClass("daughter", slots = c(c = "list"), contains = "mother") setAs(from = "daughter", to = "grandma", def = function(from) new("grandma")) setClassUnion(name = "mr_x", members = c("daughter", "mother")) is(new("daughter")) [1] "daughter" "mother" "grandma" "mr_x" Is there a reasonable explanation for this behavior? I could not find any and I would appreciate your help. If it is not an unintended behavior, I find it very confusing and hard to anticipate. Kind regads Andreas -- Prof. Dr. Andreas Bl?tte Professor of Public Policy and Regional Politics University of Duisburg-Essen [[alternative HTML version deleted]] ______________________________________________ R-devel at r-project.org<mailto:R-devel at r-project.org> mailing list https://stat.ethz.ch/mailman/listinfo/r-devel [[alternative HTML version deleted]]