0

I am working on a pet project in R that builds out a custom S3 class named groupr. I have written a few functions with dot notation (print.groupr, subset.groupr) as described in the Hadley tutorial, but I would like to extend the apply function and cannot find a way to do so.

As demonstrated in the tutorial, the pryr package provides a hint. I see that the subset and print function are different function types like so:

> pryr::ftype(subset)
[1] "s3"      "generic"
> pryr::ftype(apply)
[1] "function"

Additionally, the subset function prints this in the terminal:

function (x, ...) 
UseMethod("subset")
<bytecode: 0x115f0ab88>
<environment: namespace:base>

But the apply function prints its entire source code. I believe I understand why this is happening - the print, subset, plot, etc functions are S3 functions and apply is a boring old normal function - but I don't see any way to extend the apply function without "overwriting" the base function. For example, UseMethod("apply") points the function call to my groupr namespace when the package is loaded.

Does anyone know how to address this? Namely, the family of apply functions are not S3 objects and cannot be extended using dot notation. Does anyone know how to write apply.myclass, apply.myotherclass functions?

Alex Thompson
  • 506
  • 5
  • 13
  • `apply` is not generic. I believe that this is what makes the difference, it should be awkward to write methods to it. Like you say, you would have to *"overwrite" the base function*. – Rui Barradas Mar 30 '18 at 17:04

1 Answers1

1

You're mostly there; you do need to overwrite the original apply, but do with a generic function that calls the original as the default. So your package would have three functions: apply, which would be the new generic, apply.default, which would call base::apply, and your new apply.myclass. When your package is loaded, your version of apply would be higher in the search path, so would get used. I'm sure I've seen packages do this but can't put my hands on an example right now.

I would, though, question if this is worth it; will your users really mind that the version for groupr's is apply_groupr, for example? We all know what apply does and that's used on matrices, is this new functionality really similar enough to share the same name? That is if I see apply(my_groupr, 2, mean) will I be confused because my_groupr isn't a matrix?

Aaron left Stack Overflow
  • 36,704
  • 7
  • 77
  • 142
  • Cool, I believe that makes sense. My plan is essentially what you describe - give it a different name altogether - because it avoids that search path nonsense. However, it does seem to me that `apply` is general enough that it could be rewritten as an S3 generic function. For example, `apply.matrix`, `apply.list`, `apply.vector` could replace `apply`, `lapply`, and `vapply`. It is all just a vectorized way of looping. I guess this is just the first time I've run into weird idiosyncrasies of R itself (another being the inconsistent application of dot notation, such as with `t.data.frame`) – Alex Thompson Mar 30 '18 at 17:21
  • Speaking of weird idiosyncrasies of R, have you seen [The R Inferno](http://www.burns-stat.com/documents/books/the-r-inferno/)? – Aaron left Stack Overflow Mar 30 '18 at 17:29
  • Ahh _that_, yes, I have seen it but have yet to give it a read through. It does seem someone has a sense of humor about this stuff. – Alex Thompson Mar 30 '18 at 17:32