The R6 Class in R

3 minute read

What is a class system?

R is an object-oriented programming language with four object-oriented class systems.1 In other words, there are four ways to write object-oriented programs in R: (1) S3 (the default in R), (2) S4, (3) reference class and (4) R6.

This short note focuses on the R6 class system.

The R6 Class

Compared with the other three class systems,

  • methods2 of an R6 object3 belong to the object itself and are not generic. Specifically, functions associated with an R6 object exist under the R6 object, while functions associated with an S3 object (say) exist globally.4

  • R6 objects have public (accessible to others) and private (internal access only) members.

These two characteristics make the R6 class system much more similar to other mainstream programming languages such as Python, C++ and Java than the other three class systems.

Chapter 16 of Hadley Wickham’s book “Advanced R” provides a detailed and technical comparison among the four class systems.

Is the R6 Class System Used in Practice?

Many recent R packages use the R6 class system, including

  • the Shiny package to build web applications (e.g., dashboards) for R

  • the dplyr package for manipulating data in data frames.

Example: Why do Private Members Useful?

To use the R6 class, we need to load and attach the library R6 first.

library(R6)

The following definition of the class Simulate.Random.Variables encapsulates input and output for simulating random variables, with all information saved in an object being public. Therefore, anyone can update any publicly saved values.

Simulate.Random.Variables = R6Class(
  classname = "Simulate random variables",
  public  = list(
    random.values = NULL,
    initialize = function(random.function, num.values, ...) {
      self$random.values = random.function(num.values, ...)
    },
    summary = function() {
      cat("Mean:", mean(self$random.values), fill = TRUE)
      cat("Var :", var(self$random.values), fill = TRUE)
    }
  )
)

Create an object of the class Simulate.Random.Variables to generate 10 exponential random variables of rate 0.5.

obj = Simulate.Random.Variables$new(rexp, 10, rate = 0.5)

Show a summary of simulated random values.

obj$summary()

Output:

Mean: 1.424028
Var : 4.617927

Change random values saved in obj to a vector of 1’s and show the summary.

obj$random.values = c(1,1,1)
obj$summary()

Output:

Mean: 1
Var : 0

Therefore, setting the member random.values of obj public allows users to hamper saved information and compromises the integrity of the method summary().

To secure the integrity of method summary(), we need to set the member random.values private.

Simulate.Random.Variables = R6Class(
  classname = "Simulate random variables",
  private = list(
    random.values = NULL
  ),
  public  = list(
    initialize = function(random.function, num.values, ...) {
      private$random.values = random.function(num.values, ...)
    },
    summary = function() {
      cat("Mean:", mean(private$random.values), fill = TRUE)
      cat("Var :", var(private$random.values), fill = TRUE)
    },
    get.random.values = function() {
      return(private$random.values)
    }
  )
)

Create the object again and try to change random.values in the object.

obj = Simulate.Random.Variables$new(rexp, 10, rate = 0.5)
obj$random.values = 10

Output:

Error in obj$random.values = 10 : 
  cannot add bindings to a locked environment

This time, users cannot update random.values in obj, so the integrity of summary() is preserved.

obj$summary()

Output:

Mean: 1.713084
Var : 2.980672

Although users cannot change random.values in obj, they can access random.values through a public method that returns the value of a private member:

obj$get.random.values()

Output:

 [1] 0.2123883 0.9912447 0.0609936 0.4544109 5.1201417 0.1505100 1.4423703 3.3093614 1.9393995
[10] 3.4500235

Learn More about the R6 Class

  • Chapter 14 of Hadley Wickham’s book “Advanced R” provides an excellent overview of how to use the R6 class system.

  • The official website of the R6 class system has more details and examples about how to use this class system in R.

References

Chang W (2022). R6: Encapsulated Classes with Reference Semantics. https://r6.r-lib.org, https://github.com/r-lib/R6/.

Wickham, Hadley. Advanced r. CRC press, 2019.

Footnotes

  1. A class primarily serves four purposes: (1) polymorphism, (2) encapsulation, (3) inheritance, (4) abstraction. The wikipedia page explains these four concepts in detail. 

  2. Methods are functions associated with a class. 

  3. An object is an instance of a class. 

  4. For example, to call a function summary() associated with an R6 object r and an S3 object s, we type r$summary() for the R6 object and summary(s) for the S3 object. 

Tags:

Categories:

Updated: