In R, how can I extend generic methods from one package in another?

喜欢而已 提交于 2019-12-06 17:24:32

问题


I have a package PackageA with a generic function:

#' doWork
#' 
#' Do some work!
#'
#' @export
setGeneric(
    "doWork", 
    function(x) {

        standardGeneric("doWork")
    })

setMethod(
    "doWork", 
    signature = c("numeric"), 
    definition = function(x) {

        x == 10 # Some logic... 
    }

In PackageB, which depends on PackageA, I would like to add more methods to doWork:

#' @import PackageA
setMethod(
    "doWork", 
    signature = c("character"), 
    definition = function(x) {

        length(x) == 1 && x == "10" # Some more logic... 
    }

This works. However, it means that the user of PackageB must also library(PackageA).

This fails:

library(PackageB)

doWork("10") # Fails!

This works:

library(PackageA)
library(PackageB)

doWork("10")

I would like to use the generic from PackageA in PackageB, but not require PackageA to be loaded to use just the methods in PackageB.

How can I achieve this?


回答1:


This is actually documented, but it's not very clear; see section 1.5.6 of Writing R Extensions.

The trick is to import the generic from PackageA and then re-export it from PackageB. Using roxygen annotations, this looks like:

#' @importMethodsFrom PackageA doWork
#' @export 
setMethod(
    "doWork", 
    signature = c("character"), 
    definition = function(x) {

        length(x) == 1 && x == "10" # Some more logic... 
    })

When you call devtools::document(), this will fail unless you have first loaded PackageA (call library(PackageA)).

However, once built, PackageA is not required:

> library(PackageB)
> showMethods("doWork")
Function: doWork (package PackageA)
x="character"
x="numeric"

For reference, the auto-generated NAMESPACE file looks like:

exportMethods(doWork)
importMethodsFrom(PackageA, doWork)

This method produces no warnings about naming conflicts and so on, so it seems to be "kosher".




回答2:


This seems to work for me, but I don't see it documented so I wouldn't necessarily assume it is kosher. pckgA:

#' @export

setGeneric("doWork", function(x) standardGeneric("doWork"))
setMethod("doWork", signature = "numeric", function(x) x == 11)

and pckgB:

#' @export
#' @import pckgA

setGeneric("doWork", getGeneric("doWork", package="pckgA"))
setMethod("doWork", "character", function(x) identical(x, "10"))

The main trick was to import and re-export doWork from pckgA in pckgB. Then starting with a clean R session:

library(pckgB)
doWork("10")
# [1] TRUE
doWork("11")
# [1] FALSE
doWork(11)
# [1] TRUE
library(pckgA)
doWork(11)
# [1] TRUE
doWork("10")
# [1] TRUE

You may need to completely clear your workspace (including hidden objects) to get rid of any prior method definitions for this to actually take effect properly.



来源:https://stackoverflow.com/questions/31317366/in-r-how-can-i-extend-generic-methods-from-one-package-in-another

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!