On 16/09/2009 12:50 PM, John Chambers wrote:
Martin Kober wrote:
2009/9/15 John Chambers <j...@r-project.org>:
I'll look at this in more detail (though not for a few days), but the
essence I think is that nothing in the current version allows one to say the
S3 equivalent of

setClass("VCorpus", contains = "list", ....)

Seems a (minor, I hope) glitch that what Martin K.  tried didn't work:
   setOldClass(c("VCorpus", "Corpus", "list"))

As far as I can see, setOldClass() should understand the basic data types as
valid superclasses.
That would be really useful.

Do you think it would be feasible to automatically assume that all S3
objects "contain" their basic data type (like a list, in this case)
for purposes of S4 class matching, when no other information is given?
That doesn't seem desirable, in general, unless there's a strong argument for it.

1. It's not always the case that S3 classes should be thought of as vectors of their object type. The classes such as "lm" that use named list components instead of attributes were not intended to be used as list vectors. For example, if
  z <- lm(....)
then z[1:2] is not really meaningful, and is(z, "list") should probably be FALSE, as it currently is.

I would disagree about this. We do expect z$coefficients to work, not just coefficients(z); the former only works because z is a list. Similarly, z[["coefficients"]]. Now, I wouldn't likely ever type z[1:2], but I can see doing computations on the names and using a numeric index to subset z.

 S3 classes of this style are more like S4
classes with no data part, and object type "S4".

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.

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...@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@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel


______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel


        [[alternative HTML version deleted]]

______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

Reply via email to