8

If I a have a class called foo, then it is straightforward to overload the summary function

summary.foo = function(x, ...) print("bar")

However this technique does not work with the sd function, that is

> bar = createFooClass()
> sd.foo = function(x, ...) print("Hi")
> sd(bar)
  error: is.atomic(x) is not TRUE

What is the correct way of overloading this function?

csgillespie
  • 59,189
  • 14
  • 150
  • 185

3 Answers3

7

You can hijack any non-generic function, make it (S3) generic and set the original version to be the default version. For example:

## make an S3 generic for sd
sd <- function(x, ...) UseMethod("sd")
## take the usual definition of sd,
## and set it to be the default method
sd.default <- stats::sd
## create a method for our class "foo"
sd.foo = function(x, ...) print("Hi")

A final step, if this is in a package, is to add a ... argument to sd.default to allow passing of package checks:

formals(sd.default) <- c(formals(sd.default), alist(... = ))

giving:

> args(sd.default)
function (x, na.rm = FALSE, ...) 
NULL
> args(stats::sd)
function (x, na.rm = FALSE) 
NULL

This then gives the desired behaviour:

> bar <- 1:10
> sd(bar)
[1] 3.027650
> class(bar) <- "foo"
> sd(bar)
[1] "Hi"

This is documented in section 7.1 of the Writing R Extensions manual

Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
  • Better to email R-devel and request that `sd` (or better, `var`) be made generic. – hadley Nov 15 '11 at 16:06
  • @hadley agreed, but as well as the need for R Core to maintain this, there is a performance hit when making something generic, hence the wording and advice in the R Ext. Manual. – Gavin Simpson Nov 15 '11 at 21:21
  • I don't buy those excuses. Why should mean by generic and not var? – hadley Nov 16 '11 at 03:08
  • @hadley They aren't my excuses ;-) (and I do agree with you) Anyway you'd be a bit stuck waiting for R Core to implement this before you could use it - so the "hijacking" method is still valuable until such a time as R Core did release a stable version of R with `sd()` as a generic. – Gavin Simpson Nov 16 '11 at 09:07
  • Sorry I should've been more clear with my comment - sometimes you need to give R-core a hard time before they will implement clearly needed changes. – hadley Nov 16 '11 at 14:00
4

You need to define a new generic for sd.

The easiest way is to use S4, because it handles the default "sd" method automatically:

setClass("foo", list(a = "numeric", names = "character"))

setGeneric("sd")

setMethod("sd", "foo", 
          function(x,  na.rm = FALSE){
              print("This is a foo object!")
              callNextMethod(x@a)
              })

tf <- new("foo", a = 1:10)
sd(tf)
#[1] "This is a foo object!"
#[1] 3.027650
VitoshKa
  • 8,387
  • 3
  • 35
  • 59
1

Look at the code of sd()---it effectively dispatches internally. In other words, it is not a generic function but a plain old regular function.

The easiest may just be to modify sd() to branch on class foo.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725