Skip to content

Avoid inheritance in double dispatch #710

@lionel-

Description

@lionel-

By guarding our methods and generics with inherits_only():

vec_ptype2.data.table <- function(x, y, ..., x_arg = "x", y_arg = "y") {
  if (inherits_only(x, c("data.table", "data.frame"))) {
    UseMethod("vec_ptype2.data.table", y)
  } else {
    vec_default_ptype2(x, y, x_arg = x_arg, y_arg = y_arg)
  }
}
vec_ptype2.data.table.data.frame <- function(x, y, ..., x_arg = "x", y_arg = "y") {
  if (inherits_only(y, "data.frame")) {
    x
  } else {
    vec_default_ptype2(x, y, x_arg = x_arg, y_arg = y_arg)
  }
}

vec_ptype2.data.frame.data.table <- function(x, y, ..., x_arg = "x", y_arg = "y") {
  # The `x` check should be taken care of `vec_ptype2.data.frame()`
  if (inherits_only(x, "data.frame") && inherits_only(y, c("data.table", "data.frame"))) {
    y
  } else {
    vec_default_ptype2(x, y, x_arg = x_arg, y_arg = y_arg)
  }
}

This means that we force subclasses to write common type methods with their superclasses, but in return we gain type stability. If we allow inheritance we get:

dt <- data.table::as.data.table(mtcars)
gdf <- dplyr::group_by(mtcars, cyl)

# This would return a `dt` because `gdf` inherits from data.frame
vec_ptype2(dt, gdf)
# And this would return a `gdf` because `dt` inherits from data.frame
vec_ptype2(gdf, dt)

We should try and offer a better common type mechanism in the long term, but this approach would at least make things more predictable in the short term.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions