Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R: when to use setGeneric or export a s4 method in the namespace

I am writing a small R package with the idea to submit it to Bioconductor in the future, which is why I decided to try out s4 classes. Unfortunately I had problems understanding when I should use setGeneric or not in my package, and the documentation for the setGeneric method is for me more or less incomprehensible.

Concrete example:

  1. I created a s4 class called Foo
  2. I defined a method for the [<- operator using setMethod("[","Foo", ...)
  3. I defined a method for the as.list function using setMethod("as.list", "Foo",...)
  4. I avoided using setGenerics and exporting my methods in the namespace as I read somewhere that it's not needed for already defined generic functions

The problem now is that the [ accessor method works like a charm, but as.list is not working. Even more confusing, when I import the library BiocGenerics by typing library(BiocGenerics) at the R terminal, as.list starts to work.

Question 1: how can I be sure that [ will always work? And it is not just a coincidence because I imported some libraries?

Question 2: what should I do to make as.list work? Export the method in the namespace? use setGeneric?

Question 3: I thought that as.list started to work because setGeneric("as.list"...) was used in the BiocGenerics package, but this does not seem to be the case, reading from here: http://www.bioconductor.org/packages/release/bioc/manuals/BiocGenerics/man/BiocGenerics.pdf
So why did as.list start to work? Where was it defined?

like image 931
JavaNewbie Avatar asked Feb 03 '15 14:02

JavaNewbie


1 Answers

In an R session with only the base packages and methods loaded you can see that the generic for "[" is defined, and the generic for "as.list" is not

> getGeneric("[")
standardGeneric for "[" defined from package "base"

function (x, i, j, ..., drop = TRUE) 
standardGeneric("[", .Primitive("["))
<bytecode: 0x28f7990>
<environment: 0x28ebef0>
Methods may be defined for arguments: x, i, j, drop
Use  showMethods("[")  for currently available ones.
> getGeneric("as.list")
NULL

Loading BiocGenerics does define the generic for as.list

> suppressPackageStartupMessages(library(BiocGenerics))
> getGeneric("as.list")
standardGeneric for "as.list" defined from package "base"

function (x, ...) 
standardGeneric("as.list")
<environment: 0x5969210>
Methods may be defined for arguments: x
Use  showMethods("as.list")  for currently available ones.

If your package is destined for use in Bioconductor, then re-use the generic from BiocGenerics. Do this with the following in your NAMESPACE file

import(methods)
import(BiocGenerics)

exportMethods("[", "as.list")

and with the following in your DESCRIPTION file

Imports: methods, BiocGenerics

If you do not import the generic from BiocGenerics, then your setMethod("as.list", ...) will create it's own generic. In the context of Bioconductor, a user would have to say YourPkg::as.list(YourObject) or BiocGenerics::as.list(NotYourObject) which is clearly not a good idea.

You need to import(methods) because in your interactive trials you're relying on the methods package to be on the search() path. But (a) R CMD BATCH runs without the methods package attached and (b) it is best practice to rely on symbols that are explicitly in your name space, rather than merely on the search path (and hence subject to masking by other packages or user functions with arbitrary behavior. This applies as much to the methods package as any other.

like image 135
Martin Morgan Avatar answered Nov 13 '22 18:11

Martin Morgan