Reputation: 19960
I have encountered a curious problem when I try to set a method that was also defined in another package. An example package which demonstrates this problem can be found here.
The key is the typeof
method that I try to set. I use the setMethod
function for typeof
and it works when I build the package and try it on a trivial S4 class.
x <- new("A", x = 2)
typeof(x)
[1] "typeof was called"
[1] "myClassA"
However, if I load another package that previously also set typeof
(e.g. the bigmemory) prior to loading the s4test
package it continues to work fine if called directly.
library(bigmemory)
library(s4test)
x <- new("A", x = 2)
typeof(x)
[1] "typeof was called"
[1] "myClassA"
But, if the typeof
method is called internally by another method (e.g. nrow
see here) then it fails and only returns S4
nrow(x)
[1] "A"
attr(,"package")
[1] "s4test"
Function: typeof (package base)
x="ANY"
x="big.matrix"
x="myClass"
A connection with
description "stdout"
class "terminal"
mode "w"
text "text"
opened "opened"
can read "no"
can write "yes"
[1] "S4"
Upvotes: 8
Views: 303
Reputation: 5059
Juan Antonio was on the right track,
but it has nothing to do with Depends
and Imports
.
The answer is in the documentation entry for Methods_for_Nongenerics
(which applies to typeof
because it is not a generic function in base
):
In writing methods for an R package, it's common for these methods to apply to a function (in another package) that is not generic in that package; that is, there are no formal methods for the function in its own package, although it may have S3 methods. The programming in this case involves one extra step, to call setGeneric() to declare that the function is generic in your package.
Calls to the function in your package will then use all methods defined there or in any other loaded package that creates the same generic function. Similarly, calls to the function in those packages will use your methods.
The original version, however, remains non-generic. Calls in that package or in other packages that use that version will not dispatch your methods except for special circumstances...
You can see the effects of this by running the following in a clean R session:
environment(typeof)
<environment: namespace:base>
library(s4test)
environment(typeof)
<environment: 0x0000000017ca5e58>
After loading the package,
the typeof
function has been made generic,
but not in its original environment.
You can also see the enclosing environment of the new generic's environment:
parent.env(environment(typeof))
<environment: namespace:base>
When calling a function,
R first looks in the current environment,
and looks in enclosing environments when something is not found.
The nrow
function is part of the base
package
(and it also wasn't generic,
although that's not relevant here),
which means that it will find its own non-generic typeof
before seeing the generic one.
The documentation of Methods_for_Nongenerics
explains different scenarios,
but for your case,
you could actually do the following:
setMethod('nrow', signature(x="myClass"),
function(x) {
# find the generic version from the global environment and bind it here
typeof <- get("typeof", .GlobalEnv)
switch(typeof(x),
"myClassA" = "A rows",
"myClassB" = "B rows")
}
)
nrow(x)
[1] "typeof was called"
[1] "A rows"
Upvotes: 2
Reputation: 2047
The problem is that Depends
is not always sure. Imports
is safer than using Depends
, yo can read more about this here.
In the global enviroment your package is using bigmemory::typeof, and inside nrow base::typeof.
> base::typeof(x)
[1] "S4"
> bigmemory::typeof(x)
[1] "typeof was called"
[1] "myClassA"
You can solve this by referring directly to the function with the name of the package you want to use.
Upvotes: 0