Rev 80774 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
\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 toapply to a function (in another package) that is not generic in thatpackage; that is, there are no formal methods for the function in itsown 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 methodsdefined there or in any other loaded package that creates the samegeneric function. Similarly, calls to the function in those packageswill use your methods.The original version, however, remains non-generic. Callsin that package or in other packages that use that version will not dispatch your methodsexcept for special circumstances:\enumerate{\item If the function is one of the primitive functions that acceptmethods, the internal C implementation will dispatch methods if oneof 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, themethod will usually be dispatched as that S3 method.\item Otherwise, you will need to ensure that all calls to thefunction 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 thatcalls to the function in that package will select methods according tothe actual arguments.However, if the function was originally a non-generic in anotherpackage, calls to the function from that package will \emph{not}dispatch methods.In addition, calls from any third package that imports the non-generic versionwill also not dispatch methods.This section considers the reason and how one might deal with theconsequences.The reason is simply the \R namespace mechanism and its role inevaluating function calls.When a name (such as the name of a function) needs to be evaluated ina 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 thatpackage.Defining methods for a function in a package ensures that calls to thefunction in that package will select the methods, because a genericversion of the function is created in the namespace.Similarly, calls from another package that has or imports the genericversion will select methods.Because the generic versions are identical, all methods will beavailable in all these packages.However, calls from any package that imports the old version or justselects 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 combinethem as variables into a data frame object.It does this by calling \code{\link{as.data.frame}()}, also in thebase package, for each of the objects.A reasonable goal would be to extend the classes of objects that canbe 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, whichcontinues to call the non-generic \code{\link{as.data.frame}()} inthat package.The details of what happens and options for dealing with it depend onthe form of the function: a primitive function; a function thatdispatches 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 fromthe internal C code.There is no explicit generic function, either S3 or S4.The internal code looks for S4 methods if the firstargument, 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 relevantclasses have been defined, which should always be the case.A function dispatches S3 methods by calling\code{\link{UseMethod}()}, which does \emph{not} look forformal methods regardless of whether the first argument is an S4object 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 themethod as an S3 method, if possible. See section \sQuote{S3\dQuote{Generic} Functions}.In the third possibility, the function is defined with no expectationof methods.For example, the base package has a number of functions that computenumerical 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 specificcomputation.A generic version of the latter functions can be written and calleddirectly to define formal methods, but no code in another package thatdoes 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 thefunction supplying arguments that should dispatch methods.This may require supplying new functions that dispatch methods andthen call the function they replace.For example, if S3 methods did not work for\code{\link{as.data.frame}()}, one could call a function thatapplied 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 relevantfunctions so that they would find the generic versions.}\section{S3 \dQuote{Generic} Functions}{S3 method dispatch looks at the class of the firstargument.S3 methods are ordinary functions with the same arguments as thegeneric function.The \dQuote{signature} of an S3 method is identified by the name towhich the method is assigned, composed of the name of thegeneric 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 S4classes, there are two possibilities: either an S4 method or an S3 method with theS4 class name.The S3 method is only possible if the intended signature has thefirst argument and nothing else.In this case,the recommended approach is to define the S3 method and also supply theidentical function as the definition of the S4 method.If the S3 generic function was \code{f3(x, ...)} and the S4 class forthe 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 originalfunction will dispatch the intended method.The S4 method alone would not be called from other packages using theoriginal 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 ofinheritance, as far as possible.S3 classes can be used for any S4 method selection, provided that theS3 classes have been registered by a call to\code{\link{setOldClass}}, with that call specifying the correct S3inheritance pattern.S4 classes can be used for any S3 method selection; when an S4 objectis detected, S3 method selection uses the contents of\code{\link{extends}(class(x))} as the equivalent of the S3inheritance (the inheritance is cached after the first call).An existing S3 method may not behave as desired for an S4 subclass, inwhich case utilities such as \code{\link{asS3}} and\code{\link{S3Part}} may be useful. If the S3 method fails on the S4object, \code{asS3(x)} may be passed instead; if the object returnedby the S3 method needs to be incorporated in the S4 object, thereplacement 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 methodsthat 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_BEGINmydf1[1:2, ] # a data frame object (with extra attributes)## IGNORE_RDIFF_END## a method explicitly for "myFrame" classsetMethod("[",signature(x = "myFrame"),function (x, i, j, ..., drop = TRUE){S3Part(x) <- callNextMethod()x@timestamps <- c(Sys.time(), as.POSIXct(x@timestamps))x})## IGNORE_RDIFF_BEGINmydf1[1:2, ]## IGNORE_RDIFF_ENDsetClass("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 inheritancef3.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 purposesf3 <- function(x, ...) UseMethod("f3")f3.default <- function(x, ...) "Default f3"## S3 method (only) for classAf3.classA <- function(x, ...) "Class classA for f3"## S3 and S4 method for numericf3.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 methodssetMethod("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 genericMathFun <- function(x) { # a smarter "data.frame" method for Math groupfor (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 computationMath.data.frame <- MathFunlogIris <- log(iris)\dontshow{removeClass("classA"); removeClass("classB"); rm(x,y)removeGeneric("f3")removeClass("myDateTime")removeMethod("Math", "data.frame"); rm(Math.data.frame, MathFun, logIris)}}