[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7. Generic functions and methods

R programmers will often want to add methods for existing generic functions, and may want to add new generic functions or make existing functions generic. In this chapter we give guidelines for doing so, with examples of the problems caused by not adhering to them.

This chapter only covers the `informal' class system copied from S3, and not with the S4 (formal) methods of package methods.

The key function for methods is NextMethod, which dispatches the next method. It is quite typical for a method function to make a few changes to its arguments, dispatch to the next method, receive the results and modify them a little. An example is

 
t.data.frame <- function(x)
{
    x <- as.matrix(x)
    NextMethod("t")
}

Also consider predict.glm: it happens that in R for historical reasons it calls predict.lm directly, but in principle (and in S originally and currently) it could use NextMethod. (NextMethod seems under-used in the R sources. Do be aware that there are S/R differences in this area, and the example above works because there is a next method, the default method, not that a new method is selected when the class is changed.)

Any method a programmer writes may be invoked from another method by NextMethod, with the arguments appropriate to the previous method. Further, the programmer cannot predict which method NextMethod will pick (it might be one not yet dreamt of), and the end user calling the generic needs to be able to pass arguments to the next method. For this to work

A method must have all the arguments of the generic, including if the generic does.

It is a grave misunderstanding to think that a method needs only to accept the arguments it needs. The original S version of predict.lm did not have a argument, although predict did. It soon became clear that predict.glm needed an argument dispersion to handle over-dispersion. As predict.lm had neither a dispersion nor a argument, NextMethod could no longer be used. (The legacy, two direct calls to predict.lm, lives on in predict.glm in R, which is based on the workaround for S3 written by Venables & Ripley.)

Further, the user is entitled to use positional matching when calling the generic, and the arguments to a method called by UseMethod are those of the call to the generic. Thus

A method must have arguments in exactly the same order as the generic.

To see the scale of this problem, consider the generic function scale, defined as

 
scale <- function (x, center = TRUE, scale = TRUE)
    UseMethod("scale")

Suppose an unthinking package writer created methods such as

 
scale.foo <- function(x, scale = FALSE, ...) { }

Then for x of class "foo" the calls

 
scale(x, , TRUE)
scale(x, scale = TRUE)

would do most likely do different things, to the justifiable consternation of the end user.

To add a further twist, which default is used when a user calls scale(x) in our example? What if

 
scale.bar <- function(x, center, scale = TRUE) NextMethod("scale")

and x has class c("bar", "foo")? It is the default specified in the method that is used, but the default specified in the generic may be the one the user sees. This leads to the recommendation:

If the generic specifies defaults, all methods should use the same defaults.

An easy way to follow these recommendations is to always keep generics simple, e.g.

 
scale <- function(x, ...) UseMethod("scale")

Only add parameters and defaults to the generic if they make sense in all possible methods implementing it.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated by root on April, 26 2012 using texi2html 1.76.