[R-pkg-devel] Two packages with the same generic function

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Tue Jun 23 01:57:24 CEST 2020


On 22/06/2020 3:48 p.m., Tom Wainwright wrote:
> Yet another alternative is simply to prevent your second package from
> overriding the previously defined generic. The basic problem is the ease
> with which R allows overriding prior generic definitions (one of those bits
> of bad behavior we in the USA used to call "a Bozo No-No"), which hides all
> the previous methods, as demonstrated by the following code:
> 
>> plot(1:3)
>>> plot <- function(x, ...) UseMethod("plot")
>>> plot(1:3)
>> Error in UseMethod("plot") :
>>    no applicable method for 'plot' applied to an object of class
>> "c('integer', 'numeric')"
>>> rm(plot)
>>> plot(1:3)
> 
> 
> (Despite Murdoch's suggestion that overriding the generic SHOULD issue a
> warning, it doesn't seem to in R 4.0.1.)

Sure it does, if pkgA and pkgB both export the same name, then you get a 
warning when you attach the second one.  For example,

 > library(MASS)
 > library(dplyr)

Attaching package: ‘dplyr’

The following object is masked from ‘package:MASS’:

     select

The following objects are masked from ‘package:stats’:

     filter, lag

The following objects are masked from ‘package:base’:

     intersect, setdiff, setequal, union

Users don't get warned about overriding names in packages they've 
loaded, because that would just be irritating.

Duncan Murdoch

> 
> So, we might try protecting the generic definitions of "foo" in both
> packages by enclosing them in something like:
> 
> tryCatch(invisible(methods("foo")), error = {foo <- function(x,...)
>> UseMethod("foo")}, finally=NULL)
> 
> 
> There's probably a more elegant way to accomplish this. This relies on
> "methods" returning an error if "foo" has no defined methods, so it is not
> redefined if their are previous methods. I haven't had time to try this in
> the two-package example, but it might work, although I'm not sure how to
> handle the Namespace declarations.
> 
>    Tom Wainwright
> 
> On Mon, Jun 22, 2020 at 10:41 AM Bert Gunter <bgunter.4567 using gmail.com> wrote:
> 
>> ...
>> and just to add to the query, assume the author of pkg B did (does) not
>> know of pkg A and so, for example, could (did) not import any of pkg A's
>> content into B. Given that there are at the moment ~20,000 packages out
>> there, this does not seem to be an unreasonable assumption. One may even
>> further assume that the user may not know that (s)he has package B loaded,
>> as it may be a dependency of another package that (s)he uses. I certainly
>> don't keep track of all the dependencies of packages I use.
>>
>> Under these assumptions, is there any more convenient alternative to
>> Wolfgang's pkgA:foo(x) explicit call under such assumptions? If pkgA has a
>> long name, what might one do?
>>
>> Bert Gunter
>>
>> "The trouble with having an open mind is that people keep coming along and
>> sticking things into it."
>> -- Opus (aka Berkeley Breathed in his "Bloom County" comic strip )
>>
>>
>> On Mon, Jun 22, 2020 at 10:00 AM Viechtbauer, Wolfgang (SP) <
>> wolfgang.viechtbauer using maastrichtuniversity.nl> wrote:
>>
>>> Hi All,
>>>
>>> Let's say there are two packages pkgA and pkgB, both of which have a
>>> generic function
>>>
>>> foo <- function(x, ...)
>>>     UseMethod("foo")
>>>
>>> and pkgA has a method for objects of class "A":
>>>
>>> foo.A <- function(x, ...)
>>>     print(x)
>>>
>>> and pkgB has a method for objects of class "B":
>>>
>>> foo.B <- function(x, ...)
>>>     plot(x)
>>>
>>> Both packages export foo and their method and declare their respective S3
>>> methods, so:
>>>
>>> export(foo)
>>> export(foo.A)
>>> S3method(foo, A)
>>>
>>> in NAMESPACE of pkgA and
>>>
>>> export(foo)
>>> export(foo.B)
>>> S3method(foo, B)
>>>
>>> in NAMESPACE of pkgB.
>>>
>>> If a user loads pkgA first and then pkgB, this fails:
>>>
>>> library(pkgA)
>>> library(pkgB)
>>> x <- 1:4
>>> class(x) <- "A"
>>> foo(x)
>>>
>>> Error in UseMethod("foo") :
>>>    no applicable method for 'foo' applied to an object of class "A"
>>>
>>> and vice-versa. Of course, pkgA::foo(x) works. Aside from pkgA importing
>>> foo() or vice-versa, is there some other clever way to make this work? In
>>> earlier versions of R (at least in 3.6.3), this used to work (i.e., the
>>> generic foo() from pkgB would find method foo.A() and vice-versa), but
>> not
>>> since 4.0.0.
>>>
>>> Best,
>>> Wolfgang
>>>
>>> ______________________________________________
>>> R-package-devel using r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-package-devel
>>>
>>
>>          [[alternative HTML version deleted]]
>>
>> ______________________________________________
>> R-package-devel using r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-package-devel
>>
> 
> 	[[alternative HTML version deleted]]
> 
> ______________________________________________
> R-package-devel using r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-package-devel
>



More information about the R-package-devel mailing list