Let's assume I'm building an S3 class for which a base type of character vector fits best.
Say,
x <- structure(
.Data = c("zap", "zong"),
class = c("myClass", "character")
)
x
# [1] "zap" "zong"
# attr(,"class")
# [1] "myClass" "character"
Let's further assume that I want to retain attributes (here: class = "myClass"
) on subsetting and similar (~ idempotent?) operations.
Default behavior of R is of course to drop all attributes (except name and dim) on subsetting, but :
Attributes should generally be thought of as ephemeral (unless theyβre formalised into an S3 class)
(emphasis added, @hadley's adv-r)
This is easy to see for factors, which do retain attributes (here: levels
and class
) :
attributes(factor(c("foo", "bar"))[1])
# $levels
# [1] "bar" "foo"
#
# $class
# [1] "factor"
But out-of-the-box S3 classes don't do this :
attributes(x[1])
# NULL
My hunch is that this is so, because base R probably implemented an S3 subsetting methods for factors , and indeed, there's an Extract.factor()
/[.factor
in base R (I couldn't find the source, probably because it's in C as an internal generic?).
@hadley discusses the same thing with regard to dplyr, teaching it to retain attributes via `sloop::reconstruct() (not on CRAN yet, sadly).
So far so good, but this seems like pretty major surgery (involving internal generic [
) just for teaching some class to retain its attributes on subsetting .
The alternatives are:
- Implement
[.myClass
to make this happen (maybe writing areconstruct.myClass()
method, though sloop is not on CRAN yet). - Using the promising sticky package by @ctbrown, which seems to implement all required for 1) via it's own class. (Package is not very active recently, and still has some bugs/limitations).
What's the best practice to do this?
Is it wise to do this, or is (retaining attributes) like putting lipstick on a pig?
Ps.: Here's a related question on S-O.
Pps.: also related:
- this issue for
sloop::reconstruct()
- this issue for adv-r