Modifying R6 objects after creation

R6 classes

The {R6} package is an implementation 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.

In this post I’ll be using the word class to refer to the original class definition using R6::R6Class() and object to refer to an instantiation of that class e.g. my_object = ClassName$new().

It is possible to add a method to an existing R6 class after it has been defined, but I want to add a new method to an object (i.e. just an instance of this class).

R6 classes simple example

MyClass <- R6::R6Class(
  "MyClass",
  public = list(
    hello = function(name) { cat("Hello", name, "\n")}
  )
)
 
# Create an instance of the class 
myobj <- MyClass$new()

# Call a method on this object
myobj$hello("#RStats")
## Hello #RStats

Modifying a Class

It is possible to create a class and then modify the definition of that class afterwards. lock_class must be set to FALSE to do this.

# Create an empty class
MyClass <- R6::R6Class(
  "MyClass",
  lock_class = FALSE
)
 
# Create a new method within the class after it was defined
MyClass$set("public", "hello", function(name) { cat("Hello", name, "\n")})

# Create an instance of the class 
myobj <- MyClass$new()

# Call a method on this object
myobj$hello("#RStats")
## Hello #RStats

Adding a new method to an Object (withouth modifying the class)

It is possible to create a class, instantiate an object of that class, and then modify the contents of that object to add a new method not in the original class definition, but only to this object.

Requires:

  • lock_object = FALSE
  • In order for a new method to behave correctly, is must have the same environment as other methods in the class. This is so it can look-up public and private variables in the class. e.g. using self$
# Create an empty class
MyClass <- R6::R6Class(
  "MyClass",
  lock_object = FALSE,
  public = list(
    line_end = "!!",
    hello = function(name) { cat("Hello", name, self$line_end, "\n")}
  )
)

# Create an instance of the class 
myobj <- MyClass$new()

# Call a method on this object
myobj$hello("#RStats")
## Hello #RStats !!
# Create a method Ensure it has the right environment
myobj$goodbye <- function(name) { cat("Goodbye", name, self$line_end, "\n")}
environment(myobj$goodbye) <- myobj$.__enclos_env__

myobj$goodbye("Mike")
## Goodbye Mike !!

What’s next?

This means I can establish a base class with a certain set of methods, but I can now dynamically create a menagerie of slightly different objects which each have
different sets of methods.

I guess this is sort of similar to perhaps defining a class inheritance hierarchy except what I want to do is regularly, and dynamically, modify the methods available on existing objects during runtime.