Ramiro Magno
Ramiro Magno

Reputation: 3175

Idiom for default S4 method?

Is there any agreed upon way of writing a fallback method for an S4 generic? I mean, if no signatures are matched, do you write a function to be used as default method that throws perhaps a message() explaining valid signatures? And what return value do you use? Or do you throw an error? Or do you do nothing at all and let R throw the usual error "unable to find an inherited method for function (...)".

Any recommendation?

Upvotes: 1

Views: 155

Answers (2)

JDL
JDL

Reputation: 1654

If you can write a generic that might plausibly work for a wide variety of possible classes, then it is common to do so. For example, the (S3) default method for terms tries to find a $terms component (or attribute — note that this includes the case when the argument is an S4 object with a @terms slot) in its first argument:

function (x, ...) 
{
    v <- x$terms
    if (is.null(v)) {
        v <- attr(x, "terms")
        if (is.null(v)) 
            stop("no terms component nor attribute")
    }
    v
}

(terms happens to be an S3 generic but there is no reason why it would not still behave this way if it were S4.)

If there is no sensible thing you can do then all you can really write for a default method is a function that accepts all the arguments in the signature of the generic (plus ... in case there are some methods that have extra arguments) and just calls stop with an error message along the lines of what you suggest.

In S3, you didn't need to do this — you could just not define the default method. This is the case with predict — if you call predict(2,3) say then you will get an error no applicable method for 'predict' applied to an object of class "c('double', 'numeric')". In S4, you will need to define such a default method yourself in the call to setGeneric.

Upvotes: 1

Konrad Rudolph
Konrad Rudolph

Reputation: 545638

Yes, there’s a agreed-on way, depending on whether a good default function already exists as a non-generic or not.

1. If there’s already a non-generic function that can serve as a default

Simply make it generic:

setGeneric('function_name')

2. If there’s no non-generic default function yet

  1. Define a new generic

    setGeneric(
        'function_name',
        function (object) {
            standardGeneric('function_name')
        }
    )
    
  2. Add a default implementation with catch-all parameters

    setMethod(
        'function_name',
        signature(object = 'ANY'),
        function (object) {
            …
        }
    )
    

Upvotes: 1

Related Questions