dotsMethods             package:methods             R Documentation

The Use of "..." in Method Signatures

Description:

     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)))

