blob: c2ecd72f4775c79c5b12018e32eae81ca5affcd8 [file] [log] [blame]
\name{Methods_for_Nongenerics}
\alias{Methods_for_Nongenerics}
\title{Methods for Non-Generic Functions in Other Packages}
\description{
In writing methods for an \R package, it's common for these methods to
apply to a function (in another package) that is not generic in that
package; that is, there are no formal methods for the function in its
own package, although it may have S3 methods.
The programming in this case involves one extra step, to call
\code{\link{setGeneric}()} to declare that the function \emph{is}
generic in your package.
Calls to the function in your package will then use all methods
defined there or in any other loaded package that creates the same
generic function. Similarly, calls to the function in those packages
will use your methods.
The original version, however, remains non-generic. Calls
in that package or in other packages that use that version will not dispatch your methods
except for special circumstances:
\enumerate{
\item If the function is one of the primitive functions that accept
methods, the internal C implementation will dispatch methods if one
of the arguments is an S4 object, as should be the case.
\item If the other version of the function dispatches S3 methods
\emph{and} your methods are also registered as S3 methods, the
method will usually be dispatched as that S3 method.
\item Otherwise, you will need to ensure that all calls to the
function come from a package in which the function is generic,
perhaps by copying code to your package.
}
Details and the underlying reasons are discussed in the following sections.
}
\section{Generic and Non-Generic Calls}{
Creating methods for a function (any function) in a package means that
calls to the function in that package will select methods according to
the actual arguments.
However, if the function was originally a non-generic in another
package, calls to the function from that package will \emph{not}
dispatch methods.
In addition, calls from any third package that imports the non-generic version
will also not dispatch methods.
This section considers the reason and how one might deal with the
consequences.
The reason is simply the \R namespace mechanism and its role in
evaluating function calls.
When a name (such as the name of a function) needs to be evaluated in
a call to a function from some package, the evaluator looks first in the frame of the call,
then in the namespace of the package and then in the imports to that
package.
Defining methods for a function in a package ensures that calls to the
function in that package will select the methods, because a generic
version of the function is created in the namespace.
Similarly, calls from another package that has or imports the generic
version will select methods.
Because the generic versions are identical, all methods will be
available in all these packages.
However, calls from any package that imports the old version or just
selects it from the search list will usually \emph{not} select methods.
A an example, consider the function
\code{\link{data.frame}()} in the base package.
This function takes any number of objects as arguments and attempts to combine
them as variables into a data frame object.
It does this by calling \code{\link{as.data.frame}()}, also in the
base package, for each of the objects.
A reasonable goal would be to extend the classes of objects that can
be included in a data frame by defining methods for
\code{\link{as.data.frame}()}.
But calls to \code{\link{data.frame}()},
will still use the version of that function in the base package, which
continues to call the non-generic \code{\link{as.data.frame}()} in
that package.
The details of what happens and options for dealing with it depend on
the form of the function: a primitive function; a function that
dispatches S3 methods; or an ordinary \R function.
Primitive functions are not actual \R function objects.
They go directly to internal C code.
Some of them, however, have been implemented to recognize methods.
These functions dispatch both S4 and S3 methods from
the internal C code.
There is no explicit generic function, either S3 or S4.
The internal code looks for S4 methods if the first
argument, or either of the arguments in the case of a binary operator,
is an S4 object.
If no S4 method is found, a search is made for an S3 method.
So defining methods for these functions works as long as the relevant
classes have been defined, which should always be the case.
A function dispatches S3 methods by calling
\code{\link{UseMethod}()}, which does \emph{not} look for
formal methods regardless of whether the first argument is an S4
object or not.
This applies to the \code{\link{as.data.frame}()} example above.
To have methods called in this situation, your package must also define the
method as an S3 method, if possible. See section \sQuote{S3
\dQuote{Generic} Functions}.
In the third possibility, the function is defined with no expectation
of methods.
For example, the base package has a number of functions that compute
numerical decompositions of matrix arguments.
Some, such as \code{\link{chol}()} and \code{\link{qr}()}
are implemented to dispatch S3 methods; others, such as
\code{\link{svd}()} are implemented directly as a specific
computation.
A generic version of the latter functions can be written and called
directly to define formal methods, but no code in another package that
does not import this generic version will dispatch such methods.
In this case, you need to have the generic version used in all the indirect calls to the
function supplying arguments that should dispatch methods.
This may require supplying new functions that dispatch methods and
then call the function they replace.
For example, if S3 methods did not work for
\code{\link{as.data.frame}()}, one could call a function that
applied the generic version to all its arguments and then called
\code{\link{data.frame}()} as a replacement for that function.
If all else fails, it might be necessary to copy over the relevant
functions so that they would find the generic versions.
}
\section{S3 \dQuote{Generic} Functions}{
S3 method dispatch looks at the class of the first
argument.
S3 methods are ordinary functions with the same arguments as the
generic function.
The \dQuote{signature} of an S3 method is identified by the name to
which the method is assigned, composed of the name of the
generic function, followed by \code{"."}, followed by the name of the class.
For details, see \code{\link{UseMethod}}.
To implement a method for one of these functions corresponding to S4
classes, there are two possibilities: either an S4 method or an S3 method with the
S4 class name.
The S3 method is only possible if the intended signature has the
first argument and nothing else.
In this case,
the recommended approach is to define the S3 method and also supply the
identical function as the definition of the S4 method.
If the S3 generic function was \code{f3(x, ...)} and the S4 class for
the new method was
\code{"myClass"}:
\code{f3.myClass <- function(x, ...) { ..... }}
\code{setMethod("f3", "myClass", f3.myClass)}
Defining both methods usually ensures that all calls to the original
function will dispatch the intended method.
The S4 method alone would not be called from other packages using the
original version of the function.
On the other hand,
an S3 method alone will not be called if there is \emph{any}
eligible non-default S4 method.
S4 and S3 method selection are designed to follow compatible rules of
inheritance, as far as possible.
S3 classes can be used for any S4 method selection, provided that the
S3 classes have been registered by a call to
\code{\link{setOldClass}}, with that call specifying the correct S3
inheritance pattern.
S4 classes can be used for any S3 method selection; when an S4 object
is detected, S3 method selection uses the contents of
\code{\link{extends}(class(x))} as the equivalent of the S3
inheritance (the inheritance is cached after the first call).
An existing S3 method may not behave as desired for an S4 subclass, in
which case utilities such as \code{\link{asS3}} and
\code{\link{S3Part}} may be useful. If the S3 method fails on the S4
object, \code{asS3(x)} may be passed instead; if the object returned
by the S3 method needs to be incorporated in the S4 object, the
replacement function for \code{S3Part} may be useful.}
\references{
Chambers, John M. (2016)
\emph{Extending R},
Chapman & Hall.
(Chapters 9 and 10.)
}
\seealso{
\link{Methods_for_S3} for suggested implementation of methods
that work for both S3 and S4 dispatch.
}
\examples{
## A class that extends a registered S3 class inherits that class' S3
## methods.
setClass("myFrame", contains = "data.frame",
slots = c(timestamps = "POSIXt"))
df1 <- data.frame(x = 1:10, y = rnorm(10), z = sample(letters,10))
mydf1 <- new("myFrame", df1, timestamps = Sys.time())
## "myFrame" objects inherit "data.frame" S3 methods; e.g., for `[`
% time dependent:
## IGNORE_RDIFF_BEGIN
mydf1[1:2, ] # a data frame object (with extra attributes)
## IGNORE_RDIFF_END
## a method explicitly for "myFrame" class
setMethod("[",
signature(x = "myFrame"),
function (x, i, j, ..., drop = TRUE)
{
S3Part(x) <- callNextMethod()
x@timestamps <- c(Sys.time(), as.POSIXct(x@timestamps))
x
}
)
## IGNORE_RDIFF_BEGIN
mydf1[1:2, ]
## IGNORE_RDIFF_END
setClass("myDateTime", contains = "POSIXt")
now <- Sys.time() # class(now) is c("POSIXct", "POSIXt")
nowLt <- as.POSIXlt(now)# class(nowLt) is c("POSIXlt", "POSIXt")
mCt <- new("myDateTime", now)
mLt <- new("myDateTime", nowLt)
## S3 methods for an S4 object will be selected using S4 inheritance
## Objects mCt and mLt have different S3Class() values, but this is
## not used.
f3 <- function(x)UseMethod("f3") # an S3 generic to illustrate inheritance
f3.POSIXct <- function(x) "The POSIXct result"
f3.POSIXlt <- function(x) "The POSIXlt result"
f3.POSIXt <- function(x) "The POSIXt result"
stopifnot(identical(f3(mCt), f3.POSIXt(mCt)))
stopifnot(identical(f3(mLt), f3.POSIXt(mLt)))
## An S4 object selects S3 methods according to its S4 "inheritance"
setClass("classA", contains = "numeric",
slots = c(realData = "numeric"))
Math.classA <- function(x) { (getFunction(.Generic))(x@realData) }
setMethod("Math", "classA", Math.classA)
x <- new("classA", log(1:10), realData = 1:10)
stopifnot(identical(abs(x), 1:10))
setClass("classB", contains = "classA")
y <- new("classB", x)
stopifnot(identical(abs(y), abs(x))) # (version 2.9.0 or earlier fails here)
## an S3 generic: just for demonstration purposes
f3 <- function(x, ...) UseMethod("f3")
f3.default <- function(x, ...) "Default f3"
## S3 method (only) for classA
f3.classA <- function(x, ...) "Class classA for f3"
## S3 and S4 method for numeric
f3.numeric <- function(x, ...) "Class numeric for f3"
setMethod("f3", "numeric", f3.numeric)
## The S3 method for classA and the closest inherited S3 method for classB
## are not found.
f3(x); f3(y) # both choose "numeric" method
## to obtain the natural inheritance, set identical S3 and S4 methods
setMethod("f3", "classA", f3.classA)
f3(x); f3(y) # now both choose "classA" method
## Need to define an S3 as well as S4 method to use on an S3 object
## or if called from a package without the S4 generic
MathFun <- function(x) { # a smarter "data.frame" method for Math group
for (i in seq_len(ncol(x))[sapply(x, is.numeric)])
x[, i] <- (getFunction(.Generic))(x[, i])
x
}
setMethod("Math", "data.frame", MathFun)
## S4 method works for an S4 class containing data.frame,
## but not for data.frame objects (not S4 objects)
try(logIris <- log(iris)) #gets an error from the old method
## Define an S3 method with the same computation
Math.data.frame <- MathFun
logIris <- log(iris)
\dontshow{
removeClass("classA"); removeClass("classB"); rm(x,y)
removeGeneric("f3")
removeClass("myDateTime")
removeMethod("Math", "data.frame"); rm(Math.data.frame, MathFun, logIris)
}
}