maechler at stat.math.ethz.ch
2008-Oct-30 11:20 UTC
[Rd] (PR#13209) Arith ops dropping S4 *and* 'object' bit [Was: ...]
>>>>> "JMC" == John Chambers <jmc at r-project.org> >>>>> on Tue, 28 Oct 2008 11:50:38 -0400 writes:JMC> The asymmetry is just the symptom of a more fundamental JMC> issue: There are no operator methods currently defined JMC> for "vector" classes, either combined with each other JMC> or with a non-S4 object. JMC> The consequence is that computations drop through to JMC> the primitive C code. Not a good idea, because that JMC> code does various things to objects with attributes, JMC> some of them bizarre and most of them not what should JMC> logically happen for S4 classes. JMC> Consider two classes with slightly more content than "test": >> setClass("test1",contains = "vector", representation(label= "character")) JMC> [1] "test1" >> setClass("test2",contains = "vector", representation(flag = "logical")) JMC> [1] "test2" JMC> These two classes both inherit from "vector" but are unrelated to each JMC> other. JMC> What should happen for arithmetic and other operators JMC> combining these two classes? There's some scope for JMC> discussion, but a reasonable policy is that the vector JMC> parts should be used and a result returned that is a JMC> simple vector. What should NOT happen is that one class JMC> is retained and the other thrown away--that's not a JMC> meaningful interpretation of the two definitions JMC> here. Unfortunately, that's what does happen with the JMC> primitives. Example below. JMC> We need to develop some methods for combinations of "vector" and "ANY" JMC> reflecting what's sensible. I'll put some first attempts on r-devel and JMC> we can discuss what's wanted. (May not happen right away, but hopefully JMC> in a week or two.) That sounds very good ! Note that there's a related infelicity of primitives dealing with class-attributes that does *not* involve S4 at all. I'm adding an extra test to str() {i.e. str.default} in order to get rid of the infinite recursion in the following, but we might also consider to change the behavior of '+' here : ## str() with an "invalid object" ob <- structure(1, class = "test") # this is fine is.object(ob)# TRUE ob <- 1 + ob # << this is "broken" is.object(ob)# FALSE - hmm, .... identical(ob, unclass(ob)) # TRUE (!!!!) and as a consequence : str(ob) ## infinite recursion in R <= 2.8.0 -- Martin Maechler, ETH Zurich JMC> --------------------- JMC> Example: JMC> If the objects are equal in length, the left operand wins, or seems to: >> x1 = new("test1", 1:10, label = "Something") >> x2 = new("test2", 10:1, flag = rep(TRUE, 10)) >> x1+x2 JMC> An object of class ???test1??? JMC> [1] 11 11 11 11 11 11 11 11 11 11 JMC> Slot "label": JMC> [1] "Something" >> x2+x1 JMC> An object of class ???test2??? JMC> [1] 11 11 11 11 11 11 11 11 11 11 JMC> Slot "flag": JMC> [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE JMC> But in fact, it's weirder than that, because _all_ the attributes are JMC> retained: >> names(attributes(x1+x2)) JMC> [1] "flag" "class" "label" >> names(attributes(x2+x1)) JMC> [1] "label" "class" "flag" JMC> That was with equal lengths. Otherwise, the code uses the longer JMC> object's attributes, including the class. >> x11 = new("test1", 101:105,label = "Smaller") >> x11 +x2 JMC> An object of class ???test2??? JMC> [1] 111 111 111 111 111 106 106 106 106 106 JMC> Slot "flag": JMC> [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE >> x2+x11 JMC> An object of class ???test2??? JMC> [1] 111 111 111 111 111 106 106 106 106 106 JMC> Slot "flag": JMC> [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE >> names(attributes(x11+x2)) JMC> [1] "flag" "class" JMC> Simon Urbanek wrote: >> >> On Oct 27, 2008, at 12:25 , Robert.McGehee at geodecapital.com wrote: >> >>> Hello all, >>> It appears that for the simplest of S4 objects, z+1 does not equal 1+z. >>> Presumably this is a bug, as 1+z seems to make a malformed object (at >>> least malformed as an input to str). >> >> FWIW the difference is that z+1 has the S4 bit set, 1+z does not. The >> objects are otherwise identical. AFAICS the same behavior is >> reproducible with any binary arithmetic operator (i.e. non-S4 %op% S4 >> will produce a result with S4 bit cleared yet valid S4 attributes). >> >> Cheers, >> S >> >> >> >>> >>>> setClass("test", representation("vector")) >>> [1] "test" >>>> z <- new("test", 1) >>>> identical(z+1, 1+z) >>> [1] FALSE >>>> str(z+1) >>> Formal class 'test' [package ".GlobalEnv"] with 1 slots >>> ..@ .Data: num 2 >>>> str(1+z) >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class >>> 'test' Class 'test' Class 'test' Class 'test' Class 'test' Class 'test' >>> Class 'test' Class 'test' Error: evaluation nested too deeply: infinite >>> recursion / options(expressions=3D)? >>> >>>> R.version >>> _ =20 >>> platform x86_64-unknown-linux-gnu =20 >>> arch x86_64 =20 >>> os linux-gnu =20 >>> system x86_64, linux-gnu =20 >>> status =20 >>> major 2 =20 >>> minor 8.0 =20 >>> year 2008 =20 >>> month 10 =20 >>> day 20 =20 >>> svn rev 46754 =20 >>> language R =20 >>> version.string R version 2.8.0 (2008-10-20) >>> >>> ______________________________________________ >>> R-devel at r-project.org mailing list >>> https://stat.ethz.ch/mailman/listinfo/r-devel >>> >>> >> >> ______________________________________________ >> R-devel at r-project.org mailing list >> https://stat.ethz.ch/mailman/listinfo/r-devel >> JMC> ______________________________________________ JMC> R-devel at r-project.org mailing list JMC> https://stat.ethz.ch/mailman/listinfo/r-devel