Reputation: 6770
I was testing to see if I could support models fitted using the cplm package in emmeans, and I ran across some very odd behavior for the terms
method. Here is an example:
library(emmeans)
library(cplm)
data(mtcars)
m <- cpglm(mpg ~ cyl + disp + hp, data = mtcars)
The function emmeans:::recover_data.lm
seemed to have some promise of working correctly. It just calls the terms
method on the object and passes it on to other functions. However, it doesn't work:
> emmeans:::recover_data.lm(m)
Error in slot(x, name) :
no slot of name "terms" for this object of class "cpglm"
Digging further, I verify that terms()
does work in the global environment:
> terms(m)
mpg ~ cyl + disp + hp
... (remaining lines excluded) ...
However, it does not work in recover_data.lm
:
> debug(emmeans:::recover_data.lm)
> emmeans:::recover_data.lm(m)
...
Browse[2]> terms(object)
Error in slot(x, name) :
no slot of name "terms" for this object of class "cpglm"
Browse[2]> getMethod("terms", "cpglm")
Error in getMethod("terms", "cpglm") :
no method found for function 'terms' and signature cpglm
However...
Browse[2]> getMethod("terms", "cplm") ## cpglm inherits from cplm
Method Definition:
function (x, ...)
attr([email protected], "terms")
... etc. ...
Browse[2]> methods("terms")
[1] terms,ANY-method terms,cplm-method terms.default* terms.formula* terms.gls*
[6] terms.merMod* terms.terms*
see '?methods' for accessing help and source code
Browse[2]> selectMethod("terms", "cpglm")
Method Definition:
function (x, ...)
attr([email protected], "terms")
... etc. ...
So, in the namespace for emmeans, I can verify that we "know" that there is a terms
method for cpglm
objects, and we can find it via selectMethod()
but not via getMethod()
or by just calling terms()
. This is very odd, especially since the documentation for selectMethod
seems to promise it should work:
The function
selectMethod()
returns the method that would be selected for a call to functionf
if the arguments had classes as specified bysignature
.
Finally, I am able to verify that this seems to have something to do with a conflict in dispatching S3 methods:
> stats::terms(m)
Error in slot(x, name) :
no slot of name "terms" for this object of class "cpglm"
Is there a way to get my package emmeans to work correctly when there is a mix of S3 and S4 methods; in particular, to look for S4 methods before S3 methods?
Upvotes: 1
Views: 65
Reputation: 5059
From the documentation entry in Methods_for_S3
:
The reasons for defining both S3 and S4 methods are as follows:
- An S4 method alone will not be seen if the S3 generic function is called directly. This will be the case, for example, if some function calls unique() from a package that does not make that function an S4 generic.
The cplm
package exported an S4 method for terms
,
but not one for S3,
which you can see here.
This automatically makes terms
an S4 generic when you attach cplm
.
However, emmeans
calls the S3 generic from stats
,
and that's what causes the problem.
You can work around it by defining an S3 generic in the global environment that dispatches to the S4 generic:
terms.cpglm <- function(x, ...) {
cplm::terms(x, ...)
}
but you should let the cplm
maintainer know that he should define S3 and S4 methods if he's transforming an S3 generic into an S4 one.
Upvotes: 1