Re: [Rd] (PR#13209) Arith ops dropping S4 *and* 'object' bit [Was: ...]

From: <maechler_at_stat.math.ethz.ch>
Date: Thu, 30 Oct 2008 12:20:13 +0100 (CET)


>>>>> "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 ______________________________________________ R-devel_at_r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
Received on Thu 30 Oct 2008 - 11:27:18 GMT

Archive maintained by Robert King, hosted by the discipline of statistics at the University of Newcastle, Australia.
Archive generated by hypermail 2.2.0, at Thu 30 Oct 2008 - 11:30:58 GMT.

Mailing list information is available at https://stat.ethz.ch/mailman/listinfo/r-devel. Please read the posting guide before posting to the list.

list of date sections of archive