[R] Google's R Style Guide
Duncan Murdoch
murdoch at stats.uwo.ca
Tue Sep 1 15:29:33 CEST 2009
On 9/1/2009 8:58 AM, Martin Morgan wrote:
> Corrado wrote:
>> Thanks Duncan, Spencer,
>>
>> To clarify, the situation is:
>>
>> 1) I have no reasons to choose S3 on S4 or vice versa, or any other coding
>> convention
>> 2) Our group has not done any OO developing in R and I would be the first, so I
>> can set up the standards
>> 3) I am starting from scratch with a new package, so I do not have any code I
>> need to re-use.
>
> One consideration might be the domain in which you are doing
> development; Bioconductor for instance makes extensive use of S4 and
> your efforts at learning to develop S4 would pay off both in your own
> code and in understanding other packages.
>
>> 4) I am an R OO newbie, so whatever I can learn from the beginning what is
>> better and good for me.
>>
>> So the questions would be two:
>>
>> 1) What coding style guide should we / I follow? Is the google style guide
>> good, or is there something better / more prescriptive which makes our
>> research group life easier?
>> 2) What class type should I use? From what you two say, I should use S3
>> because is easier to use .... what are the disadvantages? Is there an
>> advantages / disadvantages table for S3 and S4 classes?
>
> It seems relevant to compare S3 and S4 code for doing S3-style
> programming, leaving more 'advanced' S4 for another day. In S3 I might
> define a simple class and method as
>
>
> makeS3Foo <-
> function(x=numeric(), y=numeric())
> {
> if (class(x) != "numeric")
> stop("'x' must be numeric")
> if (class(y) != "numeric")
> stop("'y' must be numeric")
> l <- list(x=x, y=y)
> class(l) <- "S3Foo"
> l
> }
>
> doS3 <- function(x, ...) NextMethod("doS3")
>
> doS3.default <- function(x, ...) "doS3 default"
>
> doS3.S3Foo <- function(x, ...) "doS3 of S3Foo"
>
>
> with an example of use being
>
>> doS3(makeS3Foo())
> [1] "doS3 of S3Foo"
That looks as though it was written by an S4 user. I would write it
this way (with some differences in behaviour):
S3Foo <- function(x=numeric(), y=numeric()) {
structure(list(x=as.numeric(x), y=as.numeric(y)), class="S3Foo")
}
The rest of my code would be pretty similar to yours, though I think it
should use UseMethod("doS3") rather than NextMethod("doS3").
Duncan Murdoch
>
>
> I use 'makeS3Foo' as a constructor, so that whenever I make an instance
> of what I'm calling class S3Foo, I have some guarantees about its structure.
>
> The S4 implementation might be
>
>
> setClass("S4Foo", representation(x="numeric", y="numeric"))
>
> makeS4Foo <-
> function(x = numeric(), y=numeric(), ...)
> {
> new("S4Foo", x=x, y=y, ...)
> }
>
> setGeneric("doS4", function(x, ...) standardGeneric("doS4"),
> useAsDefault=function(x, ...) "do default")
>
> setMethod("doS4", "S4Foo", function(x, ...) "doS4 of S4Foo")
>
> and use with
>
>> doS4(makeS4Foo())
> [1] "doS4 of S4Foo"
>
> It seems like the translation between the two is really quite
> transparent, and equally arcane to someone new to R.
>
> Some things I get from S4 are a level of automatic type checking
>
>> makeS4Foo(x="bar")
> Error in validObject(.Object) :
> invalid class "S4Foo" object: invalid object for slot "x" in class
> "S4Foo": got class "character", should be or extend class "numeric"
>
> a way of knowing that my 'S4Foo' conforms to expectations -- in S3 I can say
>
> l = list(a=1, b=2)
> class(l) <- "S3Foo"
>
> and have no way of knowing whether this is 'valid' or not; in S4 I would
> not use this method of creating a class (I'd use my constructor, or
> perhaps 'new' if I were being undisciplined, and get type checking as
> above) but if I did I'd be able to find
>
>> class(l) <- "S4Foo"
>> validObject(l)
> Error in validObject(l) :
> invalid class "S4Foo" object: slots in class definition but not in
> object: "x", "y"
>
> an error when I try and access data not in the class (normally I'd have
> made a constructor, and not use slot access @ directly)
>
>> makeS3Foo()$z
> NULL
>> makeS4Foo()@z
> Error: no slot of name "z" for this object of class "S4Foo"
>
> and reflection on the class
>
>> getClass("S4Foo")
> Class “S4Foo” [in ".GlobalEnv"]
>
> Slots:
>
> Name: x y
> Class: numeric numeric
>
>
> Some of the tools for documenting S3 classes and methods are more mature
> than for S4 (e.g., package.skeleton does a better job of making a
> package containing my existing S3 code objects, but I usually use
> package.skeleton to start a project, not to move existing objects into a
> new project). The fact that the class has been declared explicitly means
> that I'm expected to document it explicitly (in contrast to, say, the
> result of lm(), which is documented only as the return value of the
> function) and this then requires decisions about how to group class and
> method documentation.
>
> Using more complicated S4 features can be, well, more complicated. But
> then these features are not readily available with S3, so...
>
> To touch on a couple of other themes in this thread... Using a '.' in a
> variable name seems like a very bad idea, given the way S3 dispatch
> works. I like to think of objects as nouns and functions as verbs, and
> so prefer to capitalize class names (as though they were proper nouns)
> and lower-case function names (so they have a more dynamic sense). And
> there are many S4 style issues that are not addressed by the google doc
> -- setMethod and setGeneric indentation in particular.
>
> A great feature of emacs-ESS that I've recently discovered (thanks
> Deepayan for pointing this out, and also for command completion) is the
> C-c C-p command when looking at an Rd file during package development;
> it very nicely returns the formatted help page to emacs.
>
> Martin
>
>>
>> Thanks
>
> ______________________________________________
> R-help at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
More information about the R-help
mailing list