Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend S3 method from another package without loading the package

I am developing a package which has the function forecast.myclass. I want that function to work nicely with forecast package. I.e. when forecast package is loaded the code forecast(object) should call forecast.myclass from my package.

Since I need only generic definition of forecast from the package forecast, and I do not use any other function from the package forecast I am reluctant to include it in the Depends. So I define the generic in my package in the following way:

##'
##' @export
forecast <- function(object,...) UseMethod("forecast") 

##' @rdname forecast.midas_r
##' @method forecast midas_r
##' @export
forecast.midas_r <- function(object,newdata=NULL,method=c("static","dynamic"),insample=get_estimation_sample(object),...) {

Now everything works as expected when package forecast is not loaded. But when I load package forecast, then forecast.midas_r is not called, when doing forecast(object) where object is of class midas_r. How should I solve this problem?

like image 870
mpiktas Avatar asked Aug 29 '13 14:08

mpiktas


2 Answers

I'm not sure there's an easy solution to this. As others have said it's probably easiest to use Depends to get around this, rather than redefining a generic method.

Here's a simple example which works for me. It's largely the same as your solution, but declaring @export means you won't need to manually update the NAMESPACE file.

##' @name mean
##' @export mean.newClass
##' 
##' @method mean newClass
##'
##' @title mean for \code{newClass} object
##' @param x A \code{newClass} object
##' @param ... Additional arguments
##'
mean.newClass <- function(x, ...){
  stopifnot(class(x)=="newClass")
  return(42)
}

Then package.skeleton("newPkg"). Put file mean.R with the above contents in the directory /R of the package.

Ensure you're in the directory 1 level below, then

roxygenize("newPkg", roxygen.dir="newPkg", copy.package=F, unlink.target=F)

Now

library(devtools)
dev_mode(on=TRUE) ### don't want to have to uninstall the package later
install_local("newPkg")
library(newPkg)
x <- c(1,2)
class(x) <- "newClass"
stopifnot(mean(x)==42)
stopifnot(mean(unclass(x))==1.5)

I realize mean is a function in base but I have tested this for modifying generic functions elsewhere to give them a new method, so it should extend to your more general case also.

like image 95
dardisco Avatar answered Oct 20 '22 22:10

dardisco


The problem here is that your definition of the forecast generic is masking the definition from the forecast package, and your method is associated with your generic rather than the forecast package generic; this is just a complicated instance of two packages defining functions of the same name. The solution is to bite the bullet and Depend: on forecast, or when at the command line and both your package and forecast are attached fully resolve the function mypackage::forecast(), or Import: forecast but not make the forecast generic available to the end user except by having them require(forecast) (this might be appropriate if forecast functionality were somehow peripheral to your package, e.g., plotting in 3D when plotting in 2D was sufficient).

For what it's worth, an S4 method in PkgB defined and exported on an imported S4 generic from PkgA implicitly exposes the S4 generic to the user, so the generic is available even if Imports: PkgA is specified in the DESCRIPTION file of PkgB.

like image 33
Martin Morgan Avatar answered Oct 20 '22 22:10

Martin Morgan