Skip to content

S7 does not dispatch on base matrix via new_S3_class("matrix"); resolves to <double>/<integer>/<logical> instead #572

@frederikfabriciusbjerre

Description

Summary:

Docs say S7 “behaves like S3.” For base matrices without an explicit class attribute beyond the implicit c("matrix","array", <type>), S3 would try f.matrix before type. In S7, a method registered with new_S3_class("matrix") is not found. Dispatch instead looks for <double> / <integer> / <logical> and errors.

Minimal reproducible example:

library(S7)

# generic
example_generic <- S7::new_generic(
  "example_generic",
  dispatch_args = "x",
  fun = function(x, ...) S7::S7_dispatch()
)

# method intended for base matrices
S7::method(example_generic, S7::new_S3_class("matrix")) <- function(x, ...) {
  "matrix"
}

m <- matrix(c(0,1,0,0), 2, 2)
class(m)
# [1] "matrix" "array"

example_generic(m)
# Error: Can't find method for `example_generic(<double>)`.

Expected:
"matrix method" is called, analogous to S3 where .class2(m) returns c("matrix","array","double","numeric") and f.matrix is tried before type.

Actual:
S7 reports no method for <double>. The S3<matrix> method is ignored.

Notes and related behavior:

  • Adding a base-type method fixes it, but that forces users to implement three methods (<double>, <integer>, <logical>) instead of one for "matrix".

  • For Matrix package S4 classes, methods::getClass("Matrix") works as expected:

    if (requireNamespace("Matrix", quietly = TRUE)) {
      S7::method(example_generic, methods::getClass("Matrix")) <- function(x, ...) {
        "Matrix"
      }
      example_generic(Matrix::Matrix(m, sparse = TRUE))  # returns "Matrix"
    }

Questions / proposed resolutions:

  1. Should S7's dispatch system look at S3/S4 classes first before typeof()?

  2. If not, can S7 expose canonical base “dimension” classes like S7::class_matrix and S7::class_array to avoid three separate type methods?

    • Requiring <integer>/<double>/<logical> methods conflates matrices with atomic vectors, so dispatch can ignore matrix semantics (2D shape, dimnames, square checks) and yield behavior different from an intended matrix method.
  3. At minimum, docs should clarify that base matrices dispatch by atomic storage type, not by "matrix", and show the recommended pattern.

I am sorry, if I have misunderstood something here, but I am working on a package where methods that work for matrix objects are important.

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