jmc at r-project.org
2008-Oct-28 15:55 UTC
[Rd] Arith ops dropping S4 bit [Was: S4 object does not commute? (PR#13222)
The asymmetry is just the symptom of a more fundamental issue: There are no operator methods currently defined for "vector" classes, either combined with each other or with a non-S4 object. The consequence is that computations drop through to the primitive C code. Not a good idea, because that code does various things to objects with attributes, some of them bizarre and most of them not what should logically happen for S4 classes. Consider two classes with slightly more content than "test": > setClass("test1",contains = "vector", representation(label = "character")) [1] "test1" > setClass("test2",contains = "vector", representation(flag = "logical")) [1] "test2" These two classes both inherit from "vector" but are unrelated to each other. What should happen for arithmetic and other operators combining these two classes? There's some scope for discussion, but a reasonable policy is that the vector parts should be used and a result returned that is a simple vector. What should NOT happen is that one class is retained and the other thrown away--that's not a meaningful interpretation of the two definitions here. Unfortunately, that's what does happen with the primitives. Example below. We need to develop some methods for combinations of "vector" and "ANY" reflecting what's sensible. I'll put some first attempts on r-devel and we can discuss what's wanted. (May not happen right away, but hopefully in a week or two.) John --------------------- Example: 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 An object of class ?test1? [1] 11 11 11 11 11 11 11 11 11 11 Slot "label": [1] "Something" > x2+x1 An object of class ?test2? [1] 11 11 11 11 11 11 11 11 11 11 Slot "flag": [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE But in fact, it's weirder than that, because _all_ the attributes are retained: > names(attributes(x1+x2)) [1] "flag" "class" "label" > names(attributes(x2+x1)) [1] "label" "class" "flag" That was with equal lengths. Otherwise, the code uses the longer object's attributes, including the class. > x11 = new("test1", 101:105,label = "Smaller") > x11 +x2 An object of class ?test2? [1] 111 111 111 111 111 106 106 106 106 106 Slot "flag": [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE > x2+x11 An object of class ?test2? [1] 111 111 111 111 111 106 106 106 106 106 Slot "flag": [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE > names(attributes(x11+x2)) [1] "flag" "class" 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 >