From jmc at r-project.org  Thu Sep 17 02:07:58 2009
From: jmc at r-project.org (John Chambers)
Date: Wed, 16 Sep 2009 17:07:58 -0700
Subject: [Rd] S3 objects in S4 slots
In-Reply-To: <4AB16EE1.8000509@stats.uwo.ca>
References: <116117690909150300v4a703807se7b08b94c3f1a68d@mail.gmail.com>	<4AAF956B.7090704@fhcrc.org>	<4AAFBA9C.5090502@r-project.org>	<116117690909151723o466d0f9eh772f3d6dbffd7e90@mail.gmail.com>	<4AB11755.3010602@r-project.org>
	<4AB16EE1.8000509@stats.uwo.ca>
Message-ID: <4AB17DDE.8060901@r-project.org>

Duncan Murdoch wrote:
> On 16/09/2009 12:50 PM, John Chambers wrote:
> <snip>
>> 2. Restricting the revision to setOldClass() leads to a clean 
>> concept, and makes the S3 class more compatible with S4 classes.   
>> The proposal at the moment is to interpret the setOldClass call much 
>> like the corresponding setClass() call above.  The included "list" 
>> would be examined to see if it was a valid "data part" in S4 terms. 
>> That's a little more general than just giving an object type.  The 
>> implementation and interpretation are then simple, and the result 
>> should be consistent with the treatment of similar S4 classes.
>>> At least in this specific case, the idea is that corpus objects can be
>>> used as a list of character vectors and code does not need to know
>>> anything about the object structure as long as only the contents of
>>> the list are needed. So (again, in this specific case), I would
>>> consider "defaulting" to the underlying data type optimal.
>>>
>>>  
>>>> Martin Morgan's code is indeed a workaround.  And as Martin Maechler
>>>> commented, the intention is to make S3 and S4 classes play together 
>>>> as much
>>>> as possible.
>
> I didn't comment on this before, because I think that's a good 
> intention, and I think users appreciate all the work you're doing to 
> bring it about:  but I'd still stick by my advice not to mix S3 and 
> S4.  As a general principle it's better to keep your programming life 
> simple.  Don't take advantage of every feature the language offers, 
> work within a subset, where you're sure you understand how everything 
> works.  Someone like you or either Martin M. has a deep enough 
> knowledge of S3 and S4 and how they interact to get away with mixing 
> them, but most users don't.
>
> The original question came up because Martin K., working with S4 
> objects, wanted to make use of an object from a package that used S3.  
> I still think he would have been better off to spend a little bit of 
> time writing the function to convert the S3 object into an S4 object, 
> and then never having to worry again about the intricacies of how S4 
> inheritance interacts with S3 inheritance.

But if the S3 class lives in someone else's package we will now have two 
versions with all the well-known headaches that causes.  In  practice, 
your advice ends up being a way of discouraging the use of S4 classes 
since there are so many legacy S3 classes.

(BTW somewhat in contrast to how I felt at one time, I'm not trying to 
dump on S3 classes, after all I'm to blame for much of what's in them.  
They are a more casual way to deal with some basic ideas and have a 
lower learning barrier.  But we didn't provide the material for as 
general or as trustworthy programming with them, so one hopes that users 
will feel able to move on as they get deeper into their projects.)

Nobody is forcing people to take advantage of the increased 
compatibility but let's try to sort out any problems, as Martin Kober 
did here, rather than discouraging the effort.

John
>
> Duncan Murdoch
>
>>>>
>>>>     
>>> Thanks to Martin Morgan for the workaround, it works very well.
>>>
>>>  
>>>> Morgan wrote:
>>>>
>>>> Hi Martin Kober --
>>>> Martin Kober wrote:
>>>>
>>>>
>>>> Hello,
>>>> I am the maintainer of the stringkernels package and have come across
>>>> a problem with using S3 objects in my S4 classes.
>>>> Specifically, I have an S4 class with a slot that takes a text corpus
>>>> as a list of character vectors. tm (version 0.5) saves corpora as
>>>> lists with a class attribute of c("VCorpus", "Corpus", "list"). I
>>>> don't actually need the class-specific attributes, I only care about
>>>> the list itself.
>>>> Here's a simplified example of my problem:
>>>>
>>>>
>>>> setClass("testclass", representation(slot="list"))
>>>>
>>>>
>>>> [1] "testclass"
>>>>
>>>>
>>>> a = list(a="1", b="2")
>>>> class(a) = c("VCorpus", "Corpus", "list") # same as corpora in tm v0.5
>>>>
>>>>
>>>> This has the feel of a workaround, but
>>>> setOldClass(class(a)[1:2]); setIs("Corpus", "list")
>>>> helps here, first registering c('vCorpus', 'Corpus') as an S3 
>>>> hierarchy
>>>> and then defining the relationship between the root of the 
>>>> hierarchy and
>>>> (the S4 class) 'list'.
>>>> Martin (Morgan)
>>>>
>>>>
>>>> x = new("testclass", slot=a)
>>>>
>>>>
>>>> Error in validObject(.Object) :
>>>>   invalid class "testclass" object: 1: invalid object for slot "slot"
>>>> in class "testclass": got class "VCorpus", should be or extend class
>>>> "list"
>>>> invalid class "testclass" object: 2: invalid object for slot "slot" in
>>>> class "testclass": got class "Corpus", should be or extend class
>>>> "list"
>>>> invalid class "testclass" object: 3: invalid object for slot "slot" in
>>>> class "testclass": got class "list", should be or extend class "list"
>>>> The last line is a bit confusing here (``got class "list", should be
>>>> or extend class "list"''). There's an even more confusing error
>>>> message when I try to assign the slot later on:
>>>>
>>>>
>>>> y = new("testclass")
>>>> y at slot = a
>>>>
>>>>
>>>> Error in checkSlotAssignment(object, name, value) :
>>>>   c("assignment of an object of class \"VCorpus\" is not valid for
>>>> slot \"slot\" in an object of class \"testclass\"; is(value, \"list\")
>>>> is not TRUE", "assignment of an object of class \"Corpus\" is not
>>>> valid for slot \"slot\" in an object of class \"testclass\"; is(value,
>>>> \"list\") is not TRUE", "assignment of an object of class \"list\" is
>>>> not valid for slot \"slot\" in an object of class \"testclass\";
>>>> is(value, \"list\") is not TRUE")
>>>> The last part of the message claims that ``is(value, "list") is not
>>>> TRUE'', but is(a, "list") is certainly TRUE. (??)
>>>> On a side note, it does work when "list" is the first entry in 
>>>> class().
>>>> I tried to use setOldClass, but seemingly using list is not possible
>>>> because it does not extend oldClass, or I didn't find out how to do
>>>> it:
>>>>
>>>>
>>>> setOldClass(c("VCorpus", "Corpus", "list"))
>>>>
>>>>
>>>> Error in setOldClass(c("VCorpus", "Corpus", "list")) :
>>>>   inconsistent old-style class information for "list"; the class is
>>>> defined but does not extend "oldClass"
>>>> Intuitively I would have thought that, because the underlying data is
>>>> of type list, it would "fit" into an object slot requiring a list,
>>>> irrespective of S3 class attributes. The only thing I can think of is
>>>> a manual solution removing the class attribute.
>>>> Is there a way to define lists with S3 class attributes such that they
>>>> are accepted as lists in S4 object slots? Or any other ways to solve
>>>> this?
>>>> Thanks in advance
>>>> Best Regards
>>>> Martin
>>>> ______________________________________________
>>>> 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
>>>>
>>>>     
>>> ______________________________________________
>>> R-devel at r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>
>>>   
>>
>>     [[alternative HTML version deleted]]
>>
>> ______________________________________________
>> 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] S3 objects in S4 slots
John Chambers