Question

Suppose all of your S4 methods associated to a specific S4 generic function/method share a formal argument that is supposed to have a specific default value. Intuitively, I would state such an argument in the definition of the S4 generic (as opposed to stating it in each method definition which would seem somewhat redundant to me).

However, I noticed that this way I'm running into trouble as it seems that the default value of the formal argument is not dispatched to the methods and thus an error is thrown.

Isn't this somewhat against the idea of having a combination of a generic and methods? Why would I have to state the formal argument in each method separately again when the default value is always the same? Can I explicitly dispatch formal arguments' default values somehow?


Below you'll find a short illustration of the behavior

Generic function

setGeneric(
    name="testFoo",
    signature=c("x", "y"),
    def=function(
        x,
        y,
        do.both=FALSE,
        ...
    ) {
    standardGeneric("testFoo")       
    }
)

Method

setMethod(
    f="testFoo", 
    signature=signature(x="numeric", y="numeric"),
    definition=function(
        x,
        y
    ) { 
    if (do.both) {
        out <- list(x=x, y=y)
    } else {
        out <- x
    }
    return(out)
    }
)

Error

> testFoo(x=1, y=2)
Error in .local(x, y, ...) : object 'do.both' not found

Redundant statement of do.both fixes it

setMethod(
    f="testFoo", 
    signature=signature(x="numeric", y="numeric"),
    definition=function(
        x,
        y,
        do.both=FALSE
    ) { 
    if (do.both) {
        out <- list(x=x, y=y)
    } else {
        out <- x
    }
    return(out)
    }
)

> testFoo(x=1, y=2)
[1] 1
Was it helpful?

Solution

When you call testFoo(x=1, y=2), it is processed first by the S4 generic, which looks for a method, finds it, and dispatches to it a call that looks like this: testFoo(x=1, y=2, do.both=FALSE, ...).

In the words of ?standardGeneric:

‘standardGeneric’ dispatches the method defined for a generic function named ‘f’, using the actual arguments in the frame from which it is called.

If the method to which it dispatches that call does not take a do.both argument, the method --- just like any other R function --- throws an error. No function can process a call containing an argument foo unless it's function definition contains either (a) a formal argument foo or (b) a "dots" argument, ..., which can absorb arbitrary supplied arguments.

Basically what you've tried is no different than the following, which fails in a similarly but perhaps easier-to-see way:

testFooGeneric <- function(x=1, y=2, do.both=FALSE, ...) {
    ## The line below does essentially what standardGeneric() does
    if(is.numeric(x) & is.numeric(y)) {
        testFooMethod(x=x, y=y, do.both=do.both)
    }
}

testFooMethod <- function(x, y) {
    cat("Success!\n")
}

testFooGeneric(x=1, y=2)
# Error in testFooMethod(x = x, y = y, do.both = do.both) : 
#   unused argument(s) (do.both = do.both)

To fix the above, you need to redefine testFooMethod() in one of the following two ways, either of which will also remedy your S4 method:

## Option 1
testFooMethod <- function(x, y, do.both) {
    cat("Success!\n")
}
testFooGeneric(x=1, y=2)
# Success!

## Option 2
testFooMethod <- function(x, y, ...) {
    cat("Success!\n")
}
testFooGeneric(x=1, y=2)
## Success!
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top