Moving on from the discussion of the fact that length(x)==9 for any POSIXlt object x (which seems diabolically confusing, given that 'c' and '[' are defined for POSIXlt and have the vector-like behavior one would expect), what about having some guidelines for coding and documentation of vector-like classes? (1) a vector-like class should implement functions in groups: level 1: 'c', '[', 'length' level 2: 'x[i] <- value', 'rep', 'unique', 'duplicated' level 3: 'head', 'tail', 'sort' NA group: 'is.na' 'x[i] <- NA' 'is.na(x) <- TRUE' character coercion: 'as.character', 'as.<CLASS>.character' names group: 'names()' 'names()<-' [should '==', 'all.equal' be included anywhere] If any member of a group is implemented, then it is considered good style to implement the others. (2) conformance or deviation from this guideline should be documented on the help page for the class. These could go in a section of R-ext, and a function that automatically checks conformance could also be supplied as part of R. A rough version of such a function is attached. This would have the following benefits: (1) developers would have guidelines and tools to help them write classes that behave in a way that users expect (2) users would know better what to expect, both in general, and in specific cases where developers followed the documentation guidelines. (3) observance of the guidelines would be an indicator of software quality (no evidence of any attention to the guidelines would be a sign that the code was more of an experiment than a piece of software that was carefully engineered for widespread use.) All of the above is a rough draft that could be discussed further (e.g., should '[.<-' go in level 1 or level 2?) if there was any interest in pursuing this suggestion. Comments? -- Tony Plate PS: Here's a few examples of running an automatic vector-functionality tester on some vector-like classes in R ("basic"="level 1", "extra"="level 2", and "bonus"="level 3" functions) (this might be hard to read if line wrapping happens -- I've attached text files): > source("testVectorFunctionality.R") > library(chron) > if (exists("length.POSIXlt")) remove(list="length.POSIXlt") > > ### 'character' passes the functionality tests > res <- testVectorFunctionality(CLASS="character", verbose=FALSE) Passed all 17 basic tests, 17 extra tests, and 5 bonus tests > > ### 'numeric' passes the functionality tests > res <- testVectorFunctionality(CLASS="numeric", verbose=FALSE) Passed all 17 basic tests, 17 extra tests, and 5 bonus tests > > ### 'integer' passes the functionality tests > res <- testVectorFunctionality(CLASS="integer", verbose=FALSE) Passed all 17 basic tests, 17 extra tests, and 5 bonus tests > > ### 'Date' passes the functionality tests > res <- testVectorFunctionality(from.numeric=function(i) as.Date("2001/01/01") + i, verbose=FALSE) Passed all 17 basic tests, 17 extra tests, and 5 bonus tests > > ### chron 'times' passes the basic, but not the extra functionality tests > res <- testVectorFunctionality(from.numeric=function(i) chron(times=i), verbose=FALSE) Failed 0 of 17 basic tests, 12 of 17 extra tests, and 0 of 0 bonus tests > res <- testVectorFunctionality(from.numeric=function(i) chron(times=i), verbose=TRUE) Testing basic vector functionality for class 'times' Testing extra vector functionality for class 'times' Failed consistency check: unique(xa) == xa Failed consistency check: unique(xb) == xb Failed consistency check: unique(x0) == x0 Failed consistency check: unique(x1) == x1 Failed consistency check: unique(xA) == xA[!duplicated(xA)] Failed consistency check: rep(x1, 3) == c(x1, x1, x1) Failed consistency check: rep(xa, 3) == c(xa, xa, xa) Failed consistency check: rep(xb, 2) == c(xb, xb) Failed consistency check: rep(x1, 0) == x1[0] Failed consistency check: rep(xa, each = 3) == xa[rep(seq(len = xa.len), each = 3)] Failed consistency check: rep(xb, each = 2) == xb[rep(seq(len = xb.len), each = 2)] Failed consistency check: rep(xa, length.out = xa.len + 1) == c(xa, xa[1]) In 17 basic consistency tests on 'times', had the following outcomes: ok:17 'ok' tests (17) involved: '[':4, c:9, length:9 In 17 extra consistency tests on 'times', had the following outcomes: failure:12, ok:5 'failure' tests (12) involved: duplicated:1, rep:7, unique:5 'ok' tests (5) involved: duplicated:5 Did not perform any bonus consistency tests on 'times' > > ### chron 'dates' does not pass the basic functionality tests > res <- testVectorFunctionality(from.numeric=function(i) chron(i), verbose=FALSE) Failed 6 of 17 basic tests, 0 of 0 extra tests, and 0 of 0 bonus tests > res <- testVectorFunctionality(from.numeric=function(i) chron(i), verbose=TRUE) Testing basic vector functionality for class ['dates', 'times'] Failed consistency check: c(x1) == x1 Failed consistency check: c(x1, x0) == x1 Failed consistency check: c(x0, x1) == x1 Failed consistency check: c(xa) == xa Failed consistency check: c(xa, x0) == xa Failed consistency check: c(x0, xa) == xa In 17 basic consistency tests on ['dates', 'times'], had the following outcomes: failure:6, ok:11 'failure' tests (6) involved: c:6 'ok' tests (11) involved: '[':4, c:3, length:9 Did not perform any extra consistency tests on ['dates', 'times'] Did not perform any bonus consistency tests on ['dates', 'times'] > # The reason for the failure with c() is that it removes names on the origin in chron 'dates' > eval(quote(all.equal(c(x1), x1)), res$bindings) [1] "Attributes: < Component 3: names for current but not for target >" > attr(eval(quote(x1), res$bindings), "origin") month day year 1 1 1970 > attr(eval(quote(c(x1)), res$bindings), "origin") [1] 1 1 1970 > > ### POSIXct passes the functionality tests > res <- testVectorFunctionality(from.numeric=function(i) as.POSIXct("2001/01/01") + 24*3600*i, verbose=FALSE) Passed all 17 basic tests, 17 extra tests, and 5 bonus tests > > ### POSIXlt fails the basic functionality tests because length() for POSIXlt always returns 9 > res <- testVectorFunctionality(from.numeric=function(i) as.POSIXlt(as.POSIXlt("2001/01/01") + 24*3600*i), verbose=FALSE) Failed 9 of 17 basic tests, 0 of 0 extra tests, and 0 of 0 bonus tests > res <- testVectorFunctionality(from.numeric=function(i) as.POSIXlt(as.POSIXlt("2001/01/01") + 24*3600*i), verbose=TRUE) Testing basic vector functionality for class ['POSIXt', 'POSIXlt'] Failed consistency check: length(x1) == 1L Failed consistency check: length(x0) == 0L Failed consistency check: length(xa) == xa.len Failed consistency check: length(xb) == xb.len Failed consistency check: length(c(x1, xa)) == xa.len + x1.len Failed consistency check: length(c(x0, xa)) == xa.len Failed consistency check: length(c(xa, xb)) == xa.len + xb.len Failed consistency check: xa[-length(xa)] == xa[seq(len = xa.len - 1)] Failed consistency check: length(xa[0]) == 0L In 17 basic consistency tests on ['POSIXt', 'POSIXlt'], had the following outcomes: failure:9, ok:8 'failure' tests (9) involved: '[':2, c:3, length:9 'ok' tests (8) involved: '[':2, c:6 Did not perform any extra consistency tests on ['POSIXt', 'POSIXlt'] Did not perform any bonus consistency tests on ['POSIXt', 'POSIXlt'] > > ### define length() for POSIXlt and now POSIXlt passes the functionality tests > length.POSIXlt <- function(x) length(x$sec) > res <- testVectorFunctionality(from.numeric=function(i) as.POSIXlt(as.POSIXlt("2001/01/01") + 24*3600*i), verbose=FALSE) Passed all 17 basic tests, 17 extra tests, and 5 bonus tests > -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: TestRuns.txt Url: https://stat.ethz.ch/pipermail/r-devel/attachments/20071216/fd6d51fc/attachment.txt -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: testVectorFunctionality.R Url: https://stat.ethz.ch/pipermail/r-devel/attachments/20071216/fd6d51fc/attachment.pl