Bug 16422 - method definitions unexpectedly altered and inheritance broken after defining a "coerce" method
Summary: method definitions unexpectedly altered and inheritance broken after defining...
Alias: None
Product: R
Classification: Unclassified
Component: S4methods (show other bugs)
Version: R-devel (trunk)
Hardware: All All
: P5 critical
Assignee: R-core
Depends on:
Reported: 2015-06-12 18:56 UTC by Hervé Pagès
Modified: 2015-06-12 21:22 UTC (History)
0 users

See Also:


Note You need to log in before you can comment on or make changes to this bug.
Description Hervé Pagès 2015-06-12 18:56:55 UTC
Here we go:

  setClass("A", representation(aa="character"))
  setClass("B", contains="A")

  setMethod("length", "A", function(x) length(x@aa))
  selectMethod("length", "A")
  selectMethod("length", "B")  # the length,A method
  identical(body(selectMethod("length", "B")),
            body(selectMethod("length", "A")))  # TRUE

  setAs("B", "A",
    def=function(from) {
        if (strict)
            class(from) <- "A"
    replace=function(from, value) {
        for (what in slotNames("A"))
            slot(from, what) <- slot(value, what)

  selectMethod("length", "A")
  selectMethod("length", "B")  # x <- as(x, "A", strict = FALSE) line inserted!

So when the target is a B object, the method that is actually called is not
the length,A method anymore but an altered version!

  identical(body(selectMethod("length", "B")),
            body(selectMethod("length", "A")))  # FALSE!

From now on, most methods defined for the A class will be altered in the same
manner when the target is a B object. For example:

  setMethod("names", "A", function(x) names(x@aa))
  selectMethod("names", "A")
  selectMethod("names", "B")

This has disastrous consequences in a more complex class hierarchy (I could
provide more details if needed). What's the purpose of altering the method
definitions in such way? (I don't see this documented anywhere.) Any chance
this can go away?

For the "show" method, inheritance is broken:

  setMethod("show", "A",
    function(object) cat("an", class(object), "instance\n")
  new("A")  # show() works as expected
  new("B")  # show() uses the default method! (inheritance is broken)

Comment 1 Hervé Pagès 2015-06-12 21:22:28 UTC
One correction to this report is that it's enough to define the coerce,B,A method
in order to observe the described behaviors (altered method definitions and
broken inheritance). So the previous setAs() statement can be replaced with

  setAs("B", "A", function(from) {if (strict) class(from) <- "A"; from})

and the reported issues can still be reproduced.