From jmc at r-project.org  Tue Oct 28 16:50:38 2008
From: jmc at r-project.org (John Chambers)
Date: Tue, 28 Oct 2008 11:50:38 -0400
Subject: [Rd] Arith ops dropping S4 bit [Was: S4 object does not
 commute? (PR#13209)]
In-Reply-To: <607768E4-A332-4AD9-8F22-F89CA88B5C61@r-project.org>
References: <20081027162507.CD7BC283416A@mail.pubhealth.ku.dk>
	<607768E4-A332-4AD9-8F22-F89CA88B5C61@r-project.org>
Message-ID: <490734CE.3070607@r-project.org>

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
>



[Rd] Arith ops dropping S4 bit [Was: S4 object does not commute? (PR#13209)]
John Chambers