Skip to content

attributes of S4 objects

2 messages · Parlamis Franklin, John Chambers

#
I am having a bit of a struggle deciding when to use attributes with  
S4 objects.  Clearly the 'class' attribute will always be present.   
But it is not clear to me whether the architects of the methods  
package intend that other attributes, such as 'names', will still be  
used when the S4 implementation is complete.

I notice that, when you create a formal object from an informal one  
with attributes, the attributes are (often) assigned to the formal  
object.  For example,

setClass("Numeric", representation("numeric"))
vec <- 1:10
names(vec) <- letters[1:10]
comment(vec) <- "comment"
Vec <- new("Numeric", vec)
attributes(Vec) ## 'names' and 'comment' attributes are assigned to  
the formal object

But I also notice that, in the absence of an appropriate 'coerce'  
method, 'as(   , superclass)' will return an object without attributes

as(Vec, "numeric") ## gives an unnamed vector of mode numeric with no  
comment attribute

Because of this, I have found myself writing methods for the sole  
purpose of preserving attributes when coercing between basic data  
types and formal classes that extend them.  But the default methods  
for coercing to the basic data types clearly want attributes to be  
stripped (they do so explicitly when strict=TRUE (the default)).  I  
am thinking that maybe it was always intended that non-class  
attributes would not be used with formal objects, and that instead  
analogous slots would appear in any formal objects that extend basic  
data types (like the Dim and Dimnames attributes in the 'Matrix'  
mother class from the Matrix package).

Is that true?  Are attributes un-S4?  Any and all style advice or  
examples would be appreciated.

Franklin Parlamis
1 day later
#
Parlamis Franklin wrote:
"Can of worms" might be a good description of this situation.

The cautious approach would be to not assume attributes get carried over 
to an object that uses a basic R data type ("numeric" in your example) 
as the data in the new class.  If you need arbitrary attributes, it's 
safer to make the old-style object into a slot:
  setClass("Numeric", representation(x = "numeric"))
which should not balk at the slot having attributes.

Here's the underlying point:  There are two very different computing 
models here.  Old-style attributes were basically undisciplined; each 
object could have its own collection of attributes and these could 
appear and disappear.  Names are a good example:

 > z = 1:10
 > names(z) <- letters[1:10]
 > names(attributes(z))
[1] "names"
 > names(z) <- NULL
 > names(attributes(z))
NULL

In contrast, S4 class definitions guarantee what slots are available, 
and that they inherit from specified classes.  So if names() in the S3 
sense were made a part of the S4 object, they could not just disappear 
as in this example.  But then it's doubtful that the great mound of code 
that uses names in the S3 sense would still all work correctly for 
objects from the S4 classes.  So the safe approach is to keep the two 
ideas separate.

There are many ways to bridge the gap; for example, by having an 
initialize() method for the S4 class that takes attributes from an S3 
object and copies them into slots in the S4 object, when the attributes 
happen to be there.