4

I have a simple R6 object generator:

thing <- R6Class("youngThing",
                 private = list(
                   ..age = 0),
                 active = list(
                   age = function(){
                     private$..age <- private$..age + 1
                     private$..age
                   }
                 )
)

That gives me a simple R6 object, where ..age increases by 1 every time the active age field is called:

a_thing <- thing$new()

a_thing$age
# [1] 1

I want the object class of a_thing to change given a threshold value of the private field ..age, like this:

class(a_thing)
# [1] "youngThing" "R6"

for(timestep in 1:10){
  if(a_thing$age >5 & ! inherits(a_thing, "olderThing")){
    class(a_thing) <- c("olderThing", class(a_thing))
  }
}

class(a_thing)
# [1] "olderThing" "youngThing" "R6" 

However, I want this to happen within the object. Is there a way to include this as an active binding in the object generator, so that any object created from it will have this functionality built-in?

NB. It is preferred that the threshold class is added to the object; that it does not replace the existing classes.

jayb
  • 555
  • 3
  • 15
  • 3
    Changing class seems like a bit of an odd design choice. Why not just have a public `is_old` Boolean flag that can be interrogated as required? – Allan Cameron Nov 01 '20 at 17:42
  • 1
    I expect there to be some answers that make this possible technically, but think twice about what this means from a design perspective. What if you assign an object to a variable, then call the method so the class changes, then call a function that expects the old class with the object. This breaks some assumptions usually made about the (immutable) class of an object and could lead to problems later on. – snaut Nov 01 '20 at 17:44
  • @AllanCameron Hi, thanks for the comment. In a situation where one object is one individual, I can use an S3 generic method to implement a different method depending on the class (age) of the object (individual). – jayb Nov 02 '20 at 10:56
  • @jayb or you could have a single method that checks the age field and behaves accordingly? – Allan Cameron Nov 02 '20 at 12:48

1 Answers1

5

You can change the class of self.

library(R6)

thing <- R6Class(
  "youngThing",
  private = list(..age = 0),
  active = list(
    age = function() {
      private$..age <- private$..age + 1

      if(private$..age > 5 && !inherits(self, "olderThing")){
        class(self) <- c("olderThing", class(self))
      }
      private$..age
    }
  )
)

a_thing has it's original class while age <= 5.

a_thing <- thing$new()

a_thing$age; a_thing$age; a_thing$age; a_thing$age; a_thing$age
#> [1] 2
#> [1] 3
#> [1] 4
#> [1] 5

class(a_thing)
#> [1] "youngThing" "R6" 

Then it updates once it goes over 5.

a_thing$age
#> [1] 6

class(a_thing)
#> [1] "olderThing" "youngThing" "R6" 
Paul
  • 8,734
  • 1
  • 26
  • 36