[R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.

Iñaki Ucar |uc@r @end|ng |rom |edor@project@org
Mon May 20 01:45:06 CEST 2019

On Sun, 19 May 2019 at 23:23, Pavel Krivitsky <pavel using uow.edu.au> wrote:
> Hi, Inaki,
> On Sun, 2019-05-19 at 16:59 +0200, Iñaki Ucar wrote:
> > IMO the simplest way to do this is to check who the caller was:
> >
> > foo <- function(x) UseMethod("foo")
> > foo.bar <- function(x) {
> >   sc <- sys.call(-1)
> >   if (is.null(sc) || sc[[1]] != "foo")
> >     .Deprecated(msg="Calling 'foo.bar' directly is deprecated")
> > }
> >
> > x <- 1
> > class(x) <- "bar"
> >
> > foo(x)      # silent
> > foo.bar(x)  # a warning is issued
> f <- getS3method("foo","bar")
> f(x) # spurious warning
> foo.baz <- function(x) NextMethod("foo")
> class(x) <- c("baz","bar")
> foo(x) # spurious warning

Checking the enclosing environment and whether was called through
NextMethod respectively covers these cases too.

> > The description in the documentation means that point 3) in your list
> > goes always first, which automatically implies 2) if the generic is
> > defined in the same package.
> Are you sure which package defines the generic matters? I've just ran
> some tests with two packages and moving the generic around doesn't seem
> to affect things: the calling function determines whose method is used.

If package A defines generic foo and package B defines method foo.bar
without registering nor exporting it, then foo can't find foo.bar.

> It seems to me like there is no contradiction after all, except that I
> propose that the registered method should take precedence within a
> namespace.
> The only situation in which it would change R's behaviour would be when
> a package/namespace contains a function foo.bar() AND a NAMESPACE
> containing S3method(foo,bar,not.foo.bar) AND calls foo() on objects of
> type bar from inside the package. It is extremely unlikely to break any
> existing code.

To try to avoid changing current behaviour if foo.bar is found, R
would need to check whether the enclosing environment is identical to
the enclosing environment of the registered method, and in that case,
give precedence to the latter (which, BTW, is exactly what you need to
do to fix the first spurious warning above).

And still, funny things may happen. For example, pkgA defines generic
foo, exports foo.bar and registers other.foo.bar instead of foo.bar.
Following your proposal, if I load pkgA and call foo for an object of
class bar, other.foo.bar is called. Then I load pkgB, which registers
just another method for foo.bar, and call foo again. What happens is
that the registered method belongs now to pkgB, which is a different
namespace, so we got different precedence, and foo.bar is called

Exceptions leads us to inconsistencies like this. I can't speak for R
core, but I don't think that the use case is compelling enough to take
that path.


More information about the R-package-devel mailing list