The “...” argument in R functions is treated specially, in that it
matches zero, one or more actual arguments (and so, objects). A
mechanism has been added to R to allow “...” as the signature of a
generic function. Methods defined for such functions will be
selected and called when all the arguments matching “...”
are from the specified class or from some subclass of that class.
Using "..." in a Signature
Beginning with version 2.8.0 of R, S4 methods can be dispatched
(selected and called) corresponding to the special argument “...”.
Currently, “...” cannot be mixed with other formal arguments:
either the signature of the generic function is “...” only, or it
does not contain “...”. (This restriction may be lifted in a future
version.)
Given a suitable generic function, methods are specified in the
usual way by a call to setMethod. The method
definition must be written expecting all the arguments corresponding
to “...” to be from the class specified in the method's signature,
or from a class that extends that class (i.e., a subclass of that
class).
Typically the methods will pass “...” down to another function or
will create a list of the arguments and iterate over that. See the
examples below.
When you have a computation that is suitable for more than one existing
class, a convenient approach may be to define a union of these
classes by a call to setClassUnion. See the example
below.
Method Selection and Dispatch for "..."
See Methods for a general discussion. The following assumes
you have read the “Method Selection and Dispatch” section of
that documentation.
A method selecting on “...” is specified by a single class in the
call to setMethod. If all the actual arguments
corresponding to “...” have this class, the corresponding method is
selected directly.
Otherwise, the class of each argument and that class' superclasses are
computed, beginning with the first “...” argument. For the first
argument, eligible methods are those for any of the classes. For
each succeeding argument that introduces a class not considered previously, the eligible methods are further
restricted to those matching the argument's class or
superclasses. If no further eligible classes exist, the iteration
breaks out and the default method, if any, is selected.
At the end of the iteration, one or more methods may be eligible.
If more than one, the selection looks for the method with the least
distance to the actual arguments. For each argument, any inherited
method corresponds to a distance, available from the contains
slot of the class definition. Since the same class can arise for
more than one argument, there may be several distances associated
with it. Combining them is inevitably arbitrary: the current
computation uses the minimum distance. Thus, for example, if a
method matched one argument directly, one as first generation
superclass and another as a second generation superclass, the
distances are 0, 1 and 2. The current selection computation would
use distance 0 for this
method. In particular, this selection criterion tends to use a method that
matches exactly one or more of the arguments' class.
As with ordinary method selection, there may be multiple methods
with the same distance. A warning message is issued and one of the
methods is chosen (the first encountered, which in this case is
rather arbitrary).
Notice that, while the computation examines all arguments, the
essential cost of dispatch goes up with the number of
distinct classes among the arguments, likely to be much
smaller than the number of arguments when the latter is large.
Implementation Details
Methods dispatching on “...” were introduced in version 2.8.0 of
R. The initial implementation of the corresponding selection and
dispatch is in an R function, for flexibility while the new
mechanism is being studied. In this implementation, a local version
of setGeneric is inserted in the generic function's
environment. The local version selects a method according to the
criteria above and calls that method, from the environment of the
generic function. This is slightly different from the action taken
by the C implementation when “...” is not involved. Aside from the
extra computing time required, the method is evaluated in a true
function call, as opposed to the special context constructed by the
C version (which cannot be exactly replicated in R code.) However,
situations in which different computational results would
be obtained have not been encountered so far, and seem very
unlikely.
Methods dispatching on arguments other than “...” are cached by storing
the inherited method in the table of all methods, where it will be
found on the next selection with the same combination of classes
in the actual arguments (but not used for inheritance searches).
Methods based on “...” are also cached, but not found quite
as immediately. As noted, the selected method depends only on the
set of classes that occur in the “...” arguments. Each of
these classes can appear one or more times, so many combinations of
actual argument classes will give rise to the same effective
signature. The selection computation first computes and sorts the
distinct classes encountered. This gives a label that will be
cached in the table of all methods, avoiding any further search for
inherited classes after the first occurrence. A call to
showMethods will expose such inherited methods.
The intention is that the “...” features will be added to the
standard C code when enough experience with them has been obtained.
It is possible that at the same time, combinations of “...” with
other arguments in signatures may be supported.
References
Chambers, John M. (2008)
Software for Data Analysis: Programming with R
Springer. (For the R version.)
Chambers, John M. (1998)
Programming with Data
Springer (For the original S4 version.)
See Also
For the general discussion of methods, see Methods and links
from there.
Examples
cc <- function(...)c(...)
setGeneric("cc")
setMethod("cc", "character", function(...)paste(...))
setClassUnion("Number", c("numeric", "complex"))
setMethod("cc", "Number", function(...) sum(...))
setClass("cdate", contains = "character", representation(date = "Date"))
setClass("vdate", contains = "vector", representation(date = "Date"))
cd1 <- new("cdate", "abcdef", date = Sys.Date())
cd2 <- new("vdate", "abcdef", date = Sys.Date())
stopifnot(identical(cc(letters, character(), cd1),
paste(letters, character(), cd1))) # the "character" method
stopifnot(identical(cc(letters, character(), cd2),
c(letters, character(), cd2)))
# the default, because "vdate" doesn't extend "character"
stopifnot(identical(cc(1:10, 1+1i), sum(1:10, 1+1i))) # the "Number" method
stopifnot(identical(cc(1:10, 1+1i, TRUE), c(1:10, 1+1i, TRUE))) # the default
stopifnot(identical(cc(), c())) # no arguments implies the default method
setGeneric("numMax", function(...)standardGeneric("numMax"))
setMethod("numMax", "numeric", function(...)max(...))
# won't work for complex data
setMethod("numMax", "Number", function(...) paste(...))
# should not be selected w/o complex args
stopifnot(identical(numMax(1:10, pi, 1+1i), paste(1:10, pi, 1+1i)))
stopifnot(identical(numMax(1:10, pi, 1), max(1:10, pi, 1)))
try(numMax(1:10, pi, TRUE)) # should be an error: no default method
## A generic version of paste(), dispatching on the "..." argument:
setGeneric("paste", signature = "...")
setMethod("paste", "Number", function(..., sep, collapse) c(...))
stopifnot(identical(paste(1:10, pi, 1), c(1:10, pi, 1)))
Results
R version 3.3.1 (2016-06-21) -- "Bug in Your Hair"
Copyright (C) 2016 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.
R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.
Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.
> library(methods)
> png(filename="/home/ddbj/snapshot/RGM3/R_rel/result/methods/dotsMethods.Rd_%03d_medium.png", width=480, height=480)
> ### Name: dotsMethods
> ### Title: The Use of '...' in Method Signatures
> ### Aliases: dotsMethods
> ### Keywords: programming classes methods
>
> ### ** Examples
>
> cc <- function(...)c(...)
>
> setGeneric("cc")
[1] "cc"
>
> setMethod("cc", "character", function(...)paste(...))
[1] "cc"
>
> setClassUnion("Number", c("numeric", "complex"))
>
> setMethod("cc", "Number", function(...) sum(...))
[1] "cc"
>
> setClass("cdate", contains = "character", representation(date = "Date"))
>
> setClass("vdate", contains = "vector", representation(date = "Date"))
>
> cd1 <- new("cdate", "abcdef", date = Sys.Date())
>
> cd2 <- new("vdate", "abcdef", date = Sys.Date())
>
> stopifnot(identical(cc(letters, character(), cd1),
+ paste(letters, character(), cd1))) # the "character" method
>
> stopifnot(identical(cc(letters, character(), cd2),
+ c(letters, character(), cd2)))
> # the default, because "vdate" doesn't extend "character"
>
> stopifnot(identical(cc(1:10, 1+1i), sum(1:10, 1+1i))) # the "Number" method
>
> stopifnot(identical(cc(1:10, 1+1i, TRUE), c(1:10, 1+1i, TRUE))) # the default
>
> stopifnot(identical(cc(), c())) # no arguments implies the default method
>
> setGeneric("numMax", function(...)standardGeneric("numMax"))
[1] "numMax"
>
> setMethod("numMax", "numeric", function(...)max(...))
[1] "numMax"
> # won't work for complex data
> setMethod("numMax", "Number", function(...) paste(...))
[1] "numMax"
> # should not be selected w/o complex args
>
> stopifnot(identical(numMax(1:10, pi, 1+1i), paste(1:10, pi, 1+1i)))
> stopifnot(identical(numMax(1:10, pi, 1), max(1:10, pi, 1)))
>
> try(numMax(1:10, pi, TRUE)) # should be an error: no default method
Error in standardGeneric("numMax") :
no method or default matching the "..." arguments in numMax(1:10, pi, TRUE)
>
> ## A generic version of paste(), dispatching on the "..." argument:
> setGeneric("paste", signature = "...")
Creating a new generic function for 'paste' in the global environment
[1] "paste"
>
> setMethod("paste", "Number", function(..., sep, collapse) c(...))
[1] "paste"
>
> stopifnot(identical(paste(1:10, pi, 1), c(1:10, pi, 1)))
>
> ## Don't show:
> for(gen in c("numMax", "cc", "paste")) removeGeneric(gen)
> for(cl in c("Number", "vdate", "cdate")) removeClass(cl)
> ## End(Don't show)
>
>
>
>
>
> dev.off()
null device
1
>