Philip Leifeld
Philip Leifeld

Reputation: 2323

Import an S3 method from a package that does not define the generic function

How can I import an S3 method from an R package without depending on the package when the generic function of the method is not defined in that package?

More specifically, my btergm package (on CRAN and GitHub) imports several functions from the ergm package. It also uses the formula method (defined in ergm) of the simulate generic function (defined in the methods package). I would like to import this method from ergm. How do I do that?

I have read elsewhere that I could just add a dependency to the ergm package. But I do not want to move ergm from Imports: to Depends: in the description because the btergm package defines its own gof function, while a function with that name is also present in the ergm package. This would cause a warning that my package overwrites the gof function after loading ergm, and CRAN does not like warnings.

It kind of works without the import at the moment. However, the lme4 package also defines a formula method for the simulate generic function. If somebody loads lme4 after loading ergm, the wrong method is picked by my code. Hence the need for a proper import.

The current setup in the description file of the btergm package:

Imports: stats4, utils, methods, graphics, network (>= 1.13.0), sna (>= 2.3.2), ergm (>= 3.10.0), parallel, Matrix (>= 1.2.2), boot (>= 1.3.17), coda (>= 0.18.1), stats, ROCR (>= 1.0.7), speedglm (>= 0.3.1), igraph (>= 0.7.1), RSiena (>= 1.0.12.232), statnet.common (>= 4.2.0)
Suggests: 
    fastglm (>= 0.0.1),
    testthat
Depends: R (>= 3.5), xergm.common (>= 1.7.7), ggplot2 (>= 2.0.0)

And the relevant part from the namespace file:

import("methods")
...
importFrom("ergm", "ergmMPLE")
importFrom("ergm", "control.simulate.formula")
importFrom("ergm", "remove.offset.formula")
importFrom("ergm", "ergm.getnetwork")
importFrom("ergm", "ergm.getmodel")
importFrom("ergm", "ergm.Cprepare")
importFrom("ergm", "ergm.design")
importFrom("ergm", "ergm.pl")
importFrom("ergm", "control.ergm")
importFrom("ergm", "ergm.getglobalstats")
importFrom("ergm", "ergm.geodistdist")
importFrom("ergm", "ergm")
importFrom("ergm", "mcmc.diagnostics")

In the ergm package, the namespace export looks as follows:

S3method(simulate,formula)

How do I import this now? Is it importFrom("ergm", "simulate") or importFrom("ergm", "simulate.formula") or importFrom("ergm", "formula-method") or something completely different? The Writing R Extensions section on specifying imports does not say anything about this.

Upvotes: 0

Views: 388

Answers (1)

user2554330
user2554330

Reputation: 44867

The simulate generic is actually defined in stats, not methods. If you want to make sure the simulate.formula method from ergm is found, you need to make sure that the ergm package is loaded; your other imports from that package will ensure that.

However, if lme4 is loaded later, then its simulate.formula method will take precedence, and you'll get a message like

> library(lme4)
Loading required package: Matrix
Registered S3 method overwritten by 'lme4':
  method           from
  simulate.formula ergm

If lme4 was loaded first, you'll presumably get a warning when ergm overwrites its simulate.formula method, and probably something in lme4 will break.

There's not a lot you can do to prevent this: it's a weakness of the S3 system.

The ideal solution is for the maintainers of stats (R Core), ergm (Pavel N. Krivitsky) and lme4 (Ben Bolker) to get together and decide on what simulate.formula should do, probably putting it in stats, and one or both of the other packages would then rename their method. That's not likely to be quick.

Another possibility is for you to get the maintainer of ergm to export the method, so you can call ergm::simulate.formula explicitly.

For a workaround, you might be able to define your own function using

simulate.formula <- ergm:::simulate.formula

and not export it. Because it looks like a method defined in your own namespace, I think it will have priority over the registered methods, and your code should work. However, you will get a check warning about using :::; you might be able to get away with this by explaining the need for it in your submission message.

Upvotes: 0

Related Questions