R S3 class and name disambiguation

Viewed 183

Similar to the post on this topic for S4 classes, I am wondering about the resolution method and the conflict prevention for S3 classes.

Let me show an example. I created two packages: pkga and pkgb. Both happen to have an S3 class constructor function returning an object whose class has the same name myclass

#' @export
myclass_instance <- function() {
  structure(list(name = "pkga"), class = c("myclass"))
}


#' @export
whatever <- function(obj) {
  UseMethod("whatever", obj)
}

#' @export
whatever.default <- function(obj) {
  print("pkga::whatever.default")
}

#' @export
whatever.myclass <- function(obj) {
  print("pkga::whatever.myclass")
}

and for pkgb

#' @export
myclass_instance <- function() {
  structure(list(name = "pkgb"), class = c("myclass"))
}


#' @export
whatever <- function(obj) {
  UseMethod("whatever", obj)
}


#' @export
whatever.default <- function(obj) {
  print("pkgb::whatever.default")
}

#' @export
whatever.myclass <- function(obj) {
  print("pkgb::whatever.myclass")
}

Now, if I have both packages installed, and try the following:

> aobj<-pkga::myclass_instance()
> pkga::whatever(aobj)
[1] "pkga::whatever.myclass"
> pkga::whatever(3)
[1] "pkga::whatever.default"

This is all correct. However, if I create an object from pkgb, and then pass it to pkga::whatever it will not apply the default as I would expect (they are two completely unrelated classes that happen to have the same name), because the resolution is only made on the name

> bobj<-pkgb::myclass_instance()
> pkga::whatever(bobj)
[1] "pkga::whatever.myclass"

This can give strange effects if two unrelated libraries happen to have the same class name, and the generic name happen to conflict. While a rare and unlikely occurrence, from the formal point of view I don't like it one bit, so I was wondering if there's a way to prevent this from happening that is better than prefixing the class name with the package name, e.g.

#' @export
myclass_instance <- function() {
  structure(list(name = "pkga"), class = c("pkga::myclass"))
}


#' @export
whatever <- function(obj) {
  UseMethod("whatever", obj)
}

#' @export
whatever.default <- function(obj) {
  print("pkga::whatever.default")
}

#' @export
`whatever.pkga::myclass` <- function(obj) {
  print("pkga::whatever.myclass")
}

and similar for pkgb.

1 Answers

Packages a and b use different namespaces. S3 is built to be informal, so the only way around this is to use package name prefixing (i. e. pkg::whatever).

Fixing this would require a level of specificity that is lacking in R (apart from explicit prefixing). According to this chapter in R Packages, this is a known phenomenon.

Related