user2503795
user2503795

Reputation: 4145

Why is `predict` not a generic function?

why is predict not a generic function? isGeneric('predict') is FALSE but isGeneric('summary') and isGeneric('print') is TRUE. All of them have methods, which can be listed with methods('predict') etc? So predict is not a generic function as described here (also obvious from looking at class) but still dispatches methods depending on the object passed to it (e.g. predict.lm or predict.glm). So there is a different way R dispatches methods? How can I test whether a function has methods so that all of the examples above are true? Yes, I can test the length of methods('predict') but that produces a warning for functions without methods.

Upvotes: 4

Views: 1193

Answers (2)

StevieP
StevieP

Reputation: 1629

My general understanding of S3 generics comes from the R Language manual where I'm lead to believe that a function func cannot be a generic without calling UseMethod in its body. So, without any fancy packages, you can use the following gist:

isFuncS3Generic <- function(func) {
  funcBody <- body(func)
  sum(grepl(pattern="UseMethod", x=funcBody)) != 0
}

edit: using your litmus test:

> isFuncS3Generic(print)
[1] TRUE
> isFuncS3Generic(predict)
[1] TRUE
> isFuncS3Generic(summary)
[1] TRUE
> isFuncS3Generic(glm)
[1] FALSE

@Hadley, I'm not really sure why this is a hard problem. Would you mind elaborating?

Upvotes: 2

Spacedman
Spacedman

Reputation: 94182

For starters, none of those functions are generic by your test:

> isGeneric('predict')
[1] FALSE
> isGeneric('summary')
[1] FALSE
> isGeneric('print')
[1] FALSE

Let's try again...

> isGeneric("summary")
[1] FALSE
> require(sp)
Loading required package: sp
> isGeneric("summary")
[1] TRUE

What's going on here? Well, isGeneric only tests for S4 generic functions, and when I start R summary is an S3 generic function. If a package wants to use S4 methods and classes and there already exists an S3 generic function then it can create an S4 generic.

So, initially summary is:

> summary
function (object, ...) 
UseMethod("summary")
<bytecode: 0x9e4fc08>
<environment: namespace:base>

which is an S3 generic. I get the sp package...

> require(sp)
Loading required package: sp
> summary
standardGeneric for "summary" defined from package "base"

function (object, ...) 
standardGeneric("summary")
<environment: 0x9f9d428>
Methods may be defined for arguments: object
Use  showMethods("summary")  for currently available ones.

and now summary is an S4 standard generic.

S3 generic methods despatch (usually) by calling {generic}.{class}, and this is what UseMethod("summary") does in the S3 summary generic function.

If you want to test if a function has methods for a particular class, then you probably have to test that it has an S4 method (using the functions for S4 method metadata) and an S3 method (by looking for a function called {generic}.{class}, such as summary.glm.

Great eh?

Upvotes: 15

Related Questions