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.
By guarding our methods and generics with
inherits_only():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:
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.