Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R R6 classes and UseMethod / generic methods

I want to use R6 classes and generic methods (UseMethod) to add small objects of different classes (Small1,MyClassA and Small2,MyClassB) to public lists (MyListA and MyListB) inside the Big instance of MyClass.

This works upon creation (Big is created with Small1 and -2), but it fails afterwards:

> Big$AddObj(Small3) #produces:
Error in UseMethod("AddObj", x) :    no applicable method for 'AddObj'
 applied to an object of class "c('MyClassA', 'R6')"

I'm not quite sure what's my error. How do I call the AddObj-Method later on for an additional object? Suggestions would be very appreciated.

require('R6')

MyClass <- R6Class("MyClass",
   public = list(
     initialize = function(...) {
       for (x in list(...)) {AddObj(x)}
     },
     AddObj = function(x) {UseMethod("AddObj", x)},
     AddObj.MyClassA = function(x) {
       MyListA <<- c(MyListA, list(x))},
     AddObj.MyClassB = function(x) {
       MyListB <<- c(MyListB, list(x))},
     AddObj.default = function(x) {
       otherObjects <<- c(otherObjects, list(x))},
     Show = function() {
       print(methods(AddObj))
       if (length(MyListA)>0) print(MyListA)
       if (length(MyListB)>0) print(MyListB)
     },
     MyListA = list(),
     MyListB = list(),
     otherObjects = list()
   )
)

MyClassA <- R6Class("MyClassA",
  public = list(
    name = NA,
    initialize = function(input) {
      if (!missing(input)) name <<- as.character(input)}
  ))
MyClassB <- R6Class("MyClassB",
  public = list(
    name = NA,
    initialize = function(input) {
      if (!missing(input)) name <<- as.character(input)}
  ))


Small1 <- MyClassA$new("MyName1")
Small2 <- MyClassB$new("MyName2")
Big <- MyClass$new(Small1, Small2)
Big$Show()

Small3 <- MyClassA$new("MyNewName")
Big$AddObj(Small3)
like image 558
Wordsmyth Avatar asked Aug 05 '14 12:08

Wordsmyth


People also ask

What is UseMethod in R?

(See also the draft 'R Language Definition'.) UseMethod creates a new function call with arguments matched as they came in to the generic. Any local variables defined before the call to UseMethod are retained (unlike S). Any statements after the call to UseMethod will not be evaluated as UseMethod does not return.

What is R6 class in R?

R6 is an implemention of encapsulated object-oriented programming for R, and is a simpler, faster, lighter-weight alternative to R's built-in reference classes. This style of programming is also sometimes referred to as classical object-oriented programming.


1 Answers

There's no S3 dispatch within an R6 object, but you can use an if-else statement like so:

library('R6')

MyClass <- R6Class("MyClass",
  portable = FALSE,
  public = list(
    initialize = function(...) {
      for (x in list(...)) {AddObj(x)}
    },
    AddObj = function(x) {
      if (inherits(x, "MyClassA"))
        MyListA <<- c(MyListA, list(x))
      else if (inherits(x, "MyClassB"))
        MyListB <<- c(MyListB, list(x))
      else
        otherObjects <<- c(otherObjects, list(x))
    },
    Show = function() {
      if (length(MyListA)>0) print(MyListA)
      if (length(MyListB)>0) print(MyListB)
    },
    MyListA = list(),
    MyListB = list(),
    otherObjects = list()
  )
)

MyClassA <- R6Class("MyClassA",
  portable = FALSE,
  public = list(
    name = NA,
    initialize = function(input) {
      if (!missing(input)) name <<- as.character(input)}
  ))
MyClassB <- R6Class("MyClassB",
  portable = FALSE,
  public = list(
    name = NA,
    initialize = function(input) {
      if (!missing(input)) name <<- as.character(input)}
  ))



Small1 <- MyClassA$new("MyName1")
Small2 <- MyClassB$new("MyName2")
Big <- MyClass$new(Small1, Small2)
Big$Show()

Small3 <- MyClassA$new("MyNewName")
Big$AddObj(Small3)
Big$Show()

Also note that I used the portable=FALSE setting, which is something that was just added to the development version of R6. See https://github.com/wch/R6/issues/16 for more information about that.

UPDATE: It turns out I was slightly wrong -- there is S3 dispatch within an R6 object, but it's not used if the function is called with $, as in Big$AddObj(Small3). You could make use of it with something like: eval(quote(AddObj(Small3)), envir = Big), but that's obviously not great. Better to go with the if-else and use inherits().

like image 198
wch Avatar answered Oct 28 '22 07:10

wch