[Rd] Questions about S4 style methods and generics

Saikat DebRoy saikat@stat.wisc.edu
09 May 2002 12:10:39 -0500


>>>>> "John" == John Chambers <jmc@research.bell-labs.com> writes:

  John> Saikat DebRoy wrote:
  >> 
  >> I have some questions about S4 style methods and generics.
  >> 
  >> First of all, is there any way of using default values for arguments
  >> in the generics/methods? My own experiments show that such arguments
  >> are always ignored.

  John> Yes there is and no they are not.

  John> But there are a couple of points to keep in mind.

  John> The definition of method dispatch is that the _body_ of the selected
  John> method is evaluated, without rematching arguments.  That means that it's
  John> the default values in the (generic) function definition that will be
  John> found in the context of the call.

I was in a situation where there is no default method.

> setGeneric("foo", function(x, y=0) standardGeneric("foo"))
[1] "foo"
> setMethod("foo", signature(x="numeric", y="numeric"), function(x, y) x+y)
[1] "foo"
> foo(1, 2)
[1] 3
> foo(1)
Error in foo(1) : No direct or inherited method for function "foo" for this call

So are the default values are substituted in the function call after the
method signature is matched? I tried a few other variations. In all
cases it ended up calling the default method if any or gave the same
error if there was no default method. I then moved to three arguments
and got some surprising (to me) results.

R> setGeneric("foo", function(x=1, y=2,z=3) standardGeneric("foo"))
[1] "foo"
R> setMethod("foo", signature(x="ANY", y="ANY",z="ANY"), function(x, y,z) cat("in default method\n"))
[1] "foo"
R> setMethod("foo", signature(x="numeric", y="numeric",z="numeric"), function(x, y,z) cat("in other method\n"))
[1] "foo"
R> foo(5,6,7)
in other method
R> foo(5,6)
Error in foo(1, 2) : No matching method for function "NA" (argument "z", with class missing)
R> foo(5)
in default method
R> getMethods("foo")
x = "ANY", y = "ANY", z = "ANY":
function (x, y, z) 
cat("in default method\n")

x = "numeric", y = "numeric", z = "numeric":
function (x, y, z) 
cat("in other method\n")

x = "numeric", y = "numeric", z = "ANY":
function (x, y, z) 
cat("in default method\n")
##:    (inherited from x = "ANY", y = "ANY", z = "ANY")

x = "numeric", y = "missing":
function (x, y, z) 
cat("in default method\n")
##:    (inherited from x = "ANY", y = "ANY", z = "ANY")


  John> An example:

  John> R> bar <- function(x, y = 0)x+y
  John> R> setGeneric("bar")
  John> R> bar
  John> function (x, y = 0) 
  John> standardGeneric("bar")
  John> <environment: 0x8622a18>
  John> R> setMethod("bar", "character", function(x, y) paste(x,y))
  John> [1] "bar"
  John> R> bar(1,2)
  John> [1] 3
  John> R> bar(99)
  John> [1] 99
  John> R> bar("test")
  John> [1] "test 0"

  John> OTOH, the default expressions for the arguments in the method are
  John> available in the method object.  You can extract them by getting the
  John> method and retrieving, for example, def$y.  It seems a sufficiently
  John> asked-for item that we'll provide a tool for it.

  John> Maybe
  John>   evaluateMethodDefault("y")
  John> ??  The point is to allow access but not burden the standard method
  John> dispatch with re-matching arguments.

  >> The green book seems to be silent on this
  >> matter.
  >> 
  >> The second question is about using a non-trivial function body for
  >> generics. Page 351 of the green book gives an example of this and the
  >> documentation for setGeneric seems to suggest I should be able to do
  >> this at least by using myDispatch=TRUE. However, the current
  >> implementation seems to set the body of the generic definition to a
  >> call to standardGeneric all the time. Is this a bug or a modification
  >> of the green book description?

  John> It's a bug.  Having said that, though, nonstandard generic functions are
  John> slightly deprecated.  There are some potential efficiency enhancements
  John> for dispatch that might be introduced but that would likely depend on
  John> the function being a standard generic.

  John> Also, experience is that people get a little confused and sometimes
  John> provide a definition of the generic that doesn't call
  John> standardGeneric("foo") and so does not give them the method dispatch
  John> they expected.

  John> But it's a bug & will be fixed.  Meanwhile, there is a workaround, if
  John> you're willing to be a bit klunky.  You define the generic and then
  John> replace the body, being careful to preserve the function's environment.

  John> Another example:

  John> R> foo <- function(x) x
  John> R> setGeneric("foo")
  John> [1] "foo"
  John> R> body(foo, envir = environment(foo)) <- 
  John> + quote({cat("Here we go!\n"); standardGeneric("foo")})
  John> R> setMethod("foo", "numeric", function(x)sum(x))
  John> [1] "foo"
  John> R> foo(1:10)
  John> Here we go!
  John> [1] 55
  John> R> foo("test")
  John> Here we go!
  John> [1] "test"

  >> 
  >> Thanks,
  >> 
  >> Saikat
  >> --
  >> Department of Statistics                       Email: saikat@stat.wisc.edu
  >> University of Wisconsin - Madison              Phone: (608) 263 5948
  >> 1210 West Dayton Street                        Fax:   (608) 262 0032
  >> Madison, WI 53706-1685
  >> -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
  >> r-devel mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
  >> Send "info", "help", or "[un]subscribe"
  >> (in the "body", not the subject !)  To: r-devel-request@stat.math.ethz.ch
  >> _._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
r-devel mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
Send "info", "help", or "[un]subscribe"
(in the "body", not the subject !)  To: r-devel-request@stat.math.ethz.ch
_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._