Package-defined S4 generic covered by a base S3 generic in R packages

While developing our agop package I encountered some problems with calling S4 generic functions defined in the Matrix package, that were created from “base” S3 generics. I don’t know whether it is an R bug (tested in R 2.15 and R Under development 2013-05-19 3.1-r62765), or whether such a behavior was induced intentionally by the R team.

Note that I discuss here package development-related issues, and not the end-user ones.

The scenario was as follows:

  1. I have a package that Depends on the Matrix package.
  2. IN the package I created a function that takes an object of class Matrix (package Matrix) as argument, and calls the S4 generic function (that was my intention) t(); something like:
    test1 <- function(A) {
       stopifnot(is(A, 'Matrix'))
       t(A)
    }
    
  3. If the function had been created in the global environment, then everything would be OK. In my case, however, I get:
    > x1 <- matrix(1:10, nrow=2)
    > x2 <- Matrix(x1)
    > test1(x2)
    Error in t.default(x) : argument is not a matrix
    

    Strange, isn't it?

  4. The error message indicates that an S3 method was called here (t.default()). However, in the GlobalEnv, we have:
    > body(t)
    standardGeneric("t") # It is an S4, not S3 generic [defined in the Matrix namespace]
    

    Quite surprisingly (for me), test1() calls:

    > body(get('t', envir=baseenv()))
    UseMethod("t") # the S3 generic from the BaseEnv
    

    Why? My package's namespace is ABOVE the Matrix's namespace...

The solution is very simple - call the S4 generic by pointing the Matrix's namespace directly with the :: operator:

test2 <- function(A) {
   stopifnot(is(A, 'Matrix'))
   Matrix::t(A)
}

Still, however, I would like to know WHY we have such behavior - any ideas?

Here is an minimal example of a package exploring this issue: NamespaceTest_0.1.tar.gz (run R CMD INSTALL NamespaceTest_0.1.tar.gz).

@UPDATE: The problem is known (see e.g. this post). Some suggest using importFrom() in the NAMESPACE file. However, the above-given solution, IMHO, is much more elegant and straightforward.

--
Marek Gągolewski

Tagged with: , , , ,
Posted in Blog/R, Blog/R-bloggers