[Rd] guidelines for consistency of vector functions

Tony Plate tplate at acm.org
Mon Dec 17 07:53:36 CET 2007


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 


More information about the R-devel mailing list