[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