From pdalgd at gmail.com  Fri Jul  9 14:25:32 2010
From: pdalgd at gmail.com (Peter Dalgaard)
Date: Fri, 09 Jul 2010 14:25:32 +0200
Subject: [Rd] Strange R object
In-Reply-To: <AANLkTimztwfyk_n0me2Jh2su8yJru6H_w8kGhgsX-XiX@mail.gmail.com>
References: <AANLkTilgSg20eTTO-hgJCR_rUwXxnwy1BVG3ddI-Fl2Z@mail.gmail.com>
	<4C36E766.70106@gmail.com>
	<AANLkTimztwfyk_n0me2Jh2su8yJru6H_w8kGhgsX-XiX@mail.gmail.com>
Message-ID: <4C37153C.7020909@gmail.com>

Gabor Grothendieck wrote:
> On Fri, Jul 9, 2010 at 5:09 AM, Peter Dalgaard <pdalgd at gmail.com> wrote:
>> Gabor Grothendieck wrote:
>>> I have *** attached *** an RData file containing an R object that
>>> is acting strangely.
>>>
>>> Try this in a fresh workspace. Do not load zoo or any other package.
>>> We load the object, zz2, from the attached RData file.  It is just
>>> the number 1 with the class c("zooreg", "zoo").
>>>
>>> Now create an S3 print routine that simply prints an X when given
>>> an object of class "zoo".
>>>
>>> If we use print on the object it produces an X but not if we just
>>> enter it at the console.  Also the object is not identical to its
>>> dput output.
>>>
>>> How can such an object exist?  What is it about the object that is
>>> different from structure(1, class = c("zoo", "zooreg")) ?
>>>
>> There's a bit in the SEXP structure that is supposed to be turned on
>> when an object has an S3 class. This is where implicit print looks,
>> whereas explicit print looks, er, elsewhere. Notice that
>>
>>> is.object(zz2)
>> [1] FALSE
>>> class(zz2) <- class(zz2)
>>> zz2
>> X
>>> is.object(zz2)
>> [1] TRUE
>>
>> Whenever the same information is stored in two ways, there is a risk of
>> inconsistency, so it is not too strange that you can have an ill-formed
>> .Rdata file (if you save zz2 back out, after the above fixup, line 11
>> changes from 526 to 782, corresponding to the bit being turned on).
>>
>> I don't think it is the job of load() to verify object structures, since
>> there is no end to that task. Rather, we shouldn't create them in the
>> first place, but you give us no clues as to how that object got made.
>>
> 
> This was originally a large object in a program that uses a variety of
> packages and it took quite a long time just to narrow it down to the
> point where I had an object sufficiently small to post.  Its not even
> clear at what point the object goes bad but your class(x) <- class(x)
> trick helped a lot and I have now been able to recreate it in a simple
> manner.
> 
> Below we create a new S3 class "X" with an Ops.X and print.X method.
> We then create an object x of that class which is just 1 with a class
> of "X".  When we multiply 1*x we get the bad object.  1*x and x have
> the same dput output but compare as FALSE.  1*x is not printed by
> print.X even though it is of class "X" while x is printed by print.X .
>  If we assign 1*x to xx and use your class assignment trick (class(xx)
> <- class(xx)) then xx prints as expected even though it did not prior
> to the class assignment.
> 
>> Ops.X <- function(e1, e2) { print("Ops.X"); NextMethod(.Generic) }
>> print.X <- function(x, ...) print("print.X")
>> x <- structure(1, class = "X")
>> dput(x)
> structure(1, class = "X")
>> dput(1*x)
> [1] "Ops.X"
> structure(1, class = "X")
>> identical(x, 1*x)
> [1] "Ops.X"
> [1] FALSE
>> 1*x
> [1] "Ops.X"
> [1] 1
> attr(,"class")
> [1] "X"
>> x
> [1] "print.X"
>> xx <- 1*x
> [1] "Ops.X"
>> class(xx) <- class(xx)
>> xx
> [1] "print.X"

Or, to minimize it further:

> x <- structure(1, class="y")
> is.object(x)
[1] TRUE
> is.object(x*1)
[1] TRUE
> is.object(1*x)
[1] FALSE
> class(x*1)
[1] "y"
> class(1*x)
[1] "y"

Yup, that looks like a bug.

-- 
Peter Dalgaard
Center for Statistics, Copenhagen Business School
Phone: (+45)38153501
Email: pd.mes at cbs.dk  Priv: PDalgd at gmail.com



[Rd] Strange R object
Peter Dalgaard