7

My question:

Why is callNextMethod() not passing arguments as expected to the next method?

Situation:

Say I have two hierarchical classes foo and bar (bar is subclass of foo) for which I have a method foobar that can dispatch for both classes (i.e., has methods for both classes).

Furthermore, the method for the (sub)class bar calls the method for foo after some calculations with callNextMethod().

Both methods have the same additional argument (with default) that should be passed to the method for foo, where only it is relevant.

setClass("foo", representation(x = "numeric"))
setClass("bar", contains = "foo")

setGeneric("foobar", function(object, ...) standardGeneric("foobar"))

setMethod("foobar", "foo", function(object, another.argument = FALSE, ...) {
    print(paste("in foo-method:", another.argument))
    if (another.argument) object@x^3
    else object@x^2
})

setMethod("foobar", "bar", function(object, another.argument = FALSE, ...) {
    print(paste("in bar-method:", another.argument))
     object@x <- sqrt(object@x)
    callNextMethod()
})

Problem description:
The arguments are not passed as expected, but the default values are taken from the method definition. Specifically, in the first method the argument is as specified in the call (TRUE), however, it changes to FALSE in the next method.

o1 <- new("bar", x = 4)

foobar(o1, another.argument = TRUE)

gives

[1] "in bar-method: TRUE"
[1] "in foo-method: FALSE"
[1] 4

I want the another.argument to be passed to the next method so that it is TRUE in the call to the foo method, too.


From ?callNextMethod I get that it should work as expected (i.e., the named argument is passed as it is in the call):

For a formal argument, say x, that appears in the original call, there is a corresponding argument in the next method call equivalent to x = x. In effect, this means that the next method sees the same actual arguments, but arguments are evaluated only once.


My second question: How can I pass another.argument to the next method. (I would really like to keep default arguments in both methods)

Community
  • 1
  • 1
Henrik
  • 14,202
  • 10
  • 68
  • 91

1 Answers1

4

I think this has to do with the way a method with a signature different from the generic is defined (within a function .local)

> selectMethod(foobar, "bar")
Method Definition:

function (object, ...) 
{
    .local <- function (object, another.argument = FALSE, ...) 
    {
        print(paste("in bar-method:", another.argument))
        object@x <- sqrt(object@x)
        callNextMethod()
    }
    .local(object, ...)
}

Signatures:
        object
target  "bar" 

defined "bar" 

The work-around is to either define the generic and methods to have the same signature

setGeneric("foobar",
    function(object, another.argument=FALSE, ...) standardGeneric("foobar"),
    signature="object")

or pass the arguments explicitly to callNextMethod

setMethod("foobar", "bar", function(object, another.argument = FALSE, ...) {
    print(paste("in bar-method:", another.argument))
     object@x <- sqrt(object@x)
    callNextMethod(object, another.argument, ...)
})
Martin Morgan
  • 45,935
  • 7
  • 84
  • 112
  • Another great answer from you to a S4 question. Thanks a lot. – Henrik Aug 26 '11 at 07:58
  • Thanks. Why can't we just call it a bug rather than an "infelicity" or an "indiscretion" ... ? Since it's hurting my brain to try to figure out how the observed behavior does **not** contradict what's in the documentation? (S4 hurts my brain at the best of times.) – Ben Bolker Aug 26 '11 at 12:14
  • I fully agree (not with the part on S4 hurting the brain, though. I actually like S4). – Henrik Aug 26 '11 at 14:26
  • 1
    'bug' implies some unintentional oversight, and I don't have that insight into the author's brain. So I've removed the judgement entirely. I'd encourage a report on R-devel, otherwise the person able to change this is ignorant of the problem. – Martin Morgan Aug 26 '11 at 15:50
  • I second the suggestion of an R-devel report. IMHO this *is* a bug, because I can't think of any plausible interpretation of the documentation that is consistent with the behavior of the code. (That said, one could fix the bug by changing the documentation ...) – Ben Bolker Aug 26 '11 at 16:29
  • I wrote a message to R-devel about it yesterday (http://thread.gmane.org/gmane.comp.lang.r.devel/28806), but no answers so far. I will keep you informed if there are news. – Henrik Aug 31 '11 at 09:15