Object-Oriented Programming (OOP) in R with R6 - The Complete Guide

Estimated time:

If you want to talk about tried and tested programming paradigms, look no further than Object-Oriented Programming (OOP). The term was coined by Alan Kay way back in 1966, which means developers worldwide have been using it for more than five decades! Today we'll dive deep into the basics of OOP in R with R6. Now, what's R6? It's an R package that provides an implementation of object-oriented programming for the R language. More on that in a bit. We'll first go over some fundamental OOP theory, and then talk about specifics, such as classes, objects, constructors, access members, inheritance, and so on. <blockquote>Want to write R code that will last? <a href="https://appsilon.com/best-practices-for-durable-r-code/" target="_blank" rel="noopener">Here are best practices for durable R code</a>.</blockquote> Table of contents: <ul><li><a href="#oop-basics">Object-Oriented Programming - The Fundamentals</a></li><li><a href="#introduction">OOP in R with R6 - What is R6?</a></li><li><a href="#classes">Classes and Objects with R6</a></li><li><a href="#constructors">Class Constructors</a></li><li><a href="#public-and-private">Public and Private Class Members</a></li><li><a href="#methods">Class Methods</a></li><li><a href="#inheritance">Class Inheritance in R and R6</a></li><li><a href="#summary">Summary of OOP in R with R6</a></li></ul> <hr /> <h2 id="oop-basics">Object-Oriented Programming - The Fundamentals</h2> As the name suggests, Object-Oriented Programming revolves around <em>objects</em>. We create these objects from blueprints named <b>classes</b>. Each class has properties and methods that describe how each object behaves. It's that simple. There are only four building blocks to object-oriented programming: <ul><li><b>Classes</b>: User-defined data types that serve as blueprints for creating objects, and their attributes and methods.</li><li><b>Objects</b>: Instances of individual classes. All dogs are dogs, but each dog is different from the other. They may share some common properties.</li><li><b>Methods</b>: Functions defined inside the class that describe the behavior of objects. A dog can bark, eat, and sleep; so you can implement corresponding <code>bark()</code>, <code>eat()</code>, and <code>sleep()</code> methods.</li><li><b>Properties</b>: Things that describe a certain object. For example, a dog can be described by name, breed, hair color, weight, eye color, and so on. Those are the common properties for all dogs, but each dog can take a specific value for each property.</li></ul> Are you following so far? Good, because things are about to get slightly complicated. In addition to the four building blocks of OOP, there are also four principles you must know. These are: <ul><li><b>Encapsulation</b>: All important information is contained inside an object privately, and only selected information is exposed. No other object can access this class and change it. To keep up the analogy, think of a dog's scent. It's unique and cannot be changed by other dogs, but it can be sniffed by another.</li><li><b>Polymorphism</b>: Objects can take on more than one form. A dog is both an animal and a pet at the same time. Polymorphism allows us to perform a single action in different ways.</li><li><b>Inheritance</b>: Classes can reuse code from other classes by designing relationships (hierarchy) between them. A <i>parent</i> class of a Dog class would be an Animal, and a <i>child</i> class would be Samoyed.</li><li><b>Abstraction</b>: Objects should only reveal internal mechanisms relevant for the use of other objects. By doing so, developers can easily make changes and additions as the project scale in complexity.</li></ul> There are some programming languages that treat everything as objects, such as Ruby and Scala. There are languages designed primarily for OOP, such as Java, and Python. <b>But where does R fit in?</b> Let's explore the ins and outs of OOP in R with the R6 package. <h2 id="introduction">OOP in R with R6 - What is R6?</h2> R6 is an R package that provides an implementation of object-oriented programming for R. It's similar to R's reference classes, but it's more efficient and doesn't depend on S4 classes and the methods package. Instances of R6 classes have reference semantics and support public/private methods, active bindings, and inheritance. Next, we'll see the syntax of a typical R6 class and explain everything that goes into writing one. <h2 id="classes">Classes and Objects with R6</h2> We'll now create a simple class using OOP in R with the R6 package. To keep things simple, we'll add only a handful of properties as a list to the <code>public</code> argument. You can add properties and methods here, and they will be accessible to other objects outside the class: <pre>library(R6) Dog &lt;- R6Class(  classname = "Dog",  public = list(    name = NULL,    age = NULL,    hair_color = NULL  ) ) </pre> Now what? This is only a blueprint - and a poorly constructed one - but that's the point. You can use this blueprint to create objects from the class. Store them to a separate variable. And use <code>$new</code> every time you're creating a new class instance: <pre>d &lt;- Dog$new() print(d) </pre> Here's what gets printed to the R console: <img class="wp-image-12962 size-full" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d3e029f75851d3d0cbf4_72b6b5bd_1-3.webp" alt="Image 1 - Contents of the object created from the Dog class with R6" width="658" height="248" /> Image 1 - Contents of the object created from the Dog class We've mentioned this class is poorly constructed because you can't pass values to <code>name</code>, <code>age</code>, or <code>hair_color</code> properties. We'll need a special method called <b>constructor</b> for that. Just for reference, here's what happens if you try to pass values to the properties of a class that doesn't have a constructor: <pre>d &lt;- Dog$new(name = "Milo", age = 4, hair_color = "black") print(d) </pre> <img class="size-full wp-image-12964" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d3e06778869364e9d626_c25ce7dd_2-3.webp" alt="Image 2 - Passing property values to a class without a constructor" width="1186" height="126" /> Image 2 - Passing property values to a class without a constructor It's a no-go. Let's see how to work with constructors in R6 next. <h2 id="constructors">Class Constructors</h2> A constructor is a special method that's executed whenever you create a new object of the class. So, each time you run <code>d &lt;- Dog$new()</code>, a new instance of the <code>Dog</code> class is created, and therefore, the constructor method is executed. In R and R6, the constructor is defined with the <code>initialize</code> function. Its job is, in the most simple terms, to bind values passed in by the user to the instance properties - <code>name</code>, <code>age</code>, and <code>hair_color</code> in our case. Constructors can do other things, but this is what you'll do every time. In R6, use <code>self$property = property</code> to attach values passed by the user: <pre>Dog &lt;- R6Class(  classname = "Dog",  public = list(    name = NULL,    age = NULL,    hair_color = NULL,    initialize = function(name = NA, age = NA, hair_color = NA) {      self$name = name      self$age = age      self$hair_color = hair_color    }  ) ) </pre> We can now use the code from the previous section to create an instance of a <code>Dog</code> class with its respective property values: <pre>d &lt;- Dog$new(name = "Milo", age = 4, hair_color = "black") print(d) </pre> <img class="size-full wp-image-12966" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d3e1b6953e1bff26064c_b4a87df3_3-3.webp" alt="Image 3 - Contents of the object created from the Dog class (2)" width="1148" height="294" /> Image 3 - Contents of the object created from the Dog class (2) You can now also access individual properties and methods of the class. For example, this is how you can print Milo's name: <pre>print(d$name) </pre> <img class="size-full wp-image-12968" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7d3e22eafb5d24630e835_220d6066_4-3.webp" alt="Image 4 - Accessing individual properties" width="298" height="90" /> Image 4 - Accessing individual properties Further, you can change property values belonging to an object by assigning a new value: <pre>d$age = 5 print(d$age) </pre> <img class="size-full wp-image-12970" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2aca3122d296e6e2819eb_5-3.webp" alt="Image 5 - Assigning new property value" width="226" height="90" /> Image 5 - Assigning new property value Now we're getting somewhere, but there's still a lot of ground to cover. Next, let's see how private and public access modifiers work in OOP in R with R6. <h2 id="public-and-private">Public and Private Class Members</h2> The R6 package allows you to define private fields and methods, in addition to the public ones. What private means is that fields and methods can only be accessed within the class, and not from the outside. To access private fields and methods within the class, use <code>private$property</code> instead of <code>self$property</code>. That's the only difference. We'll demonstrate how the private access modifier works by making all of our three properties private. This means we'll have to change how we access the properties in the constructor: <pre>Dog &lt;- R6Class(  classname = "Dog",  public = list(    initialize = function(name = NA, age = NA, hair_color = NA) {      private$name = name      private$age = age      private$hair_color = hair_color    }  ),  private = list(    name = NULL,    age = NULL,    hair_color = NULL  ) ) </pre> Let's create the same object from the class as before: <pre>d &lt;- Dog$new(name = "Milo", age = 4, hair_color = "black") print(d) </pre> <img class="size-full wp-image-12972" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2ab94385f15aced653f94_6-2.webp" alt="Image 6 - Instantiating a class with private elements" width="1158" height="334" /> Image 6 - Instantiating a class with private elements The console output is now significantly different. We see everything available inside and outside the class. We also see the values assigned to each property. <strong>B</strong><b>ut can we access them?</b> Well, no, that's the point of a private access modifier: <pre>print(d$age) </pre> <img class="size-full wp-image-12974" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2abf5f99b81a7b8ed69ca_7-2.webp" alt="Image 7 - Attempting to access private class members outside the class" width="272" height="84" /> Image 7 - Attempting to access private class members outside the class Now you know the distinction between private and public in R and R6. We still haven't covered one essential topic - methods. Let's see how to declare both public and private ones. <h2 id="methods">Class Methods</h2> We've managed to cover a lot of ground without even discussing class methods. Put simply, these are equivalent to plain old R functions with one twist - they belong to a class. Like properties, methods can be both public and private. Let's see how they work. The snippet below adds three methods to our <code>Dog</code> class: <ul><li><code>dog_age()</code> - Private method which returns the age of a dog multiplied by 7.</li><li><code>bark()</code> - Public method that prints the name of the dog with a barking message. For demonstrations' sake, we'll call it from the constructor.</li><li><code>show()</code> - Public method that prints details of a dog - its name, age, and hair color.</li></ul> <pre>Dog &lt;- R6Class(  classname = "Dog",  public = list(    initialize = function(name = NA, age = NA, hair_color = NA) {      private$name = name      private$age = age      private$hair_color = hair_color      self$bark()    },    bark = function() {      cat(private$name, " says Woof!", sep = "")    },    show = function() {      cat("Dog: \n")      cat("\tName: ", private$name, "\n", sep = "")      cat("\tAge: ", private$age, " or ", private$dog_age(), " in dog years\n", sep = "")      cat("\tHair color: ", private$hair_color, "\n", sep = "")    }  ),  private = list(    name = NULL,    age = NULL,    hair_color = NULL,    dog_age = function() {      return(private$age * 7)    }  ) ) </pre> And now let's make an instance of this class. You'll immediately see a message printed to the console, as the <code>bark()</code> method was called from the constructor: <pre>d &lt;- Dog$new(name = "Milo", age = 4, hair_color = "black") </pre> <img class="size-full wp-image-12976" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2ac0355e80e7996bbc3d6_8-2.webp" alt="Image 8 - Creating an instance of the class" width="1094" height="86" /> Image 8 - Creating an instance of the class Remember that <code>show()</code> is a public method, which means you can call it from outside the class. It will print the details about our dog: <pre>d$show() </pre> <img class="size-full wp-image-12978" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2aca536b8b6c75562109f_9-1.webp" alt="Image 9 - Calling a public method outside the class" width="630" height="170" /> Image 9 - Calling a public method outside the class The same logic won't work if you try to call a private method. You can't access it outside the class, so you'll get an error instead: <pre>d$dog_age() </pre> <img class="size-full wp-image-12980" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2ac4f2404f12fca8abcd5_10-1.webp" alt="Image 10 - Attempting to call a private method outside the class" width="674" height="90" /> Image 10 - Attempting to call a private method outside the class And that's how methods work in OOP in R with R6. There's one topic left for discussion today, and that's inheritance. <h2 id="inheritance">Class Inheritance in R and R6</h2> One R6 class can inherit from another, which means we can model the parent-child relationship. For example, all dogs are animals, but not all animals are dogs. Therefore, almost every animal has legs, but a dog is almost certain to have four of them. For the sake of demonstration, we'll create an <code>Animal</code> class that describes the basic behavior of any animal. Every animal has a name (sort of), age, and a number of legs (not necessarily, but let's stick with that logic). Also, let's assume every animal can make a sound. A <code>Dog</code> can then inherit all the properties from the <code>Animal</code> class and add its own. For example, we'll add <code>hair_color</code> property to the <code>Dog</code> class. Code-wise, the whole thing boils down to a couple of new things: <ul><li><code>inherit</code> - A parameter that specifies from which class the child class will inherit from.</li><li><code>super$initialize</code> - The way we call the constructor of the parent class. This is needed if we want to modify the constructor.</li></ul> Here's the code for both classes: <pre>Animal &lt;- R6Class(  classname = "Animal",  public = list(    initialize = function(name = NA, age = NA, number_of_legs = NA) {      private$name = name      private$age = age      private$number_of_legs = number_of_legs    },    make_sound = function(sound) {      cat(private$name, " says ", sound, "\n", sep = "")    }  ),  private = list(    name = NULL,    age = NULL,    number_of_legs = NULL  ) ) Dog &lt;- R6Class(  classname = "Dog",  inherit = Animal,  public = list(    initialize = function(name = NA, age = NA, number_of_legs = NA, hair_color = NA) {      super$initialize(name, age, number_of_legs)      private$hair_color = hair_color    }  ),  private = list(    hair_color = NULL  ) ) </pre> Let's make an instance of the <code>Dog</code> class and see what happens: <pre>d &lt;- Dog$new(name = "Milo", age = 4, number_of_legs = 4, hair_color = "black") print(d) </pre> <img class="size-full wp-image-12982" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2ac50ab96f43a3110e2ed_11-1.webp" alt="Image 11 - Creating an instance of the child class" width="1542" height="464" /> Image 11 - Creating an instance of the child class We can see from the console output that our class inherits from some other class, alongside the public and private members. Let's try calling the <code>make_sound()</code> function that's only available in the parent class. If our logic is correct, any instance of the <code>Dog</code> class will have access to it: <pre>d$make_sound(sound = "Woooof!") </pre> <img class="size-full wp-image-12984" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2ac118644ca583e462057_12.webp" alt="Image 12 - Accessing parent methods from a child class" width="606" height="86" /> Image 12 - Accessing parent methods from a child class That's the essence of inheritance. We model general behaviors in a parent class, and then inherit and slightly modify them in a child class. Easy! <hr /> <h2 id="summary">Summary of OOP in R with R6</h2> We won't dive any deeper into object-oriented programming with R and R6 today. We covered a lot of ground, and this alone should be enough for you to model real-world problems and real-world behaviors. At Appsilon, we find R super-flexible if you want to use an object-oriented programming paradigm, even though the language itself wasn't necessarily designed for it. There's nothing you can do in Java that you can't do in R, even though the syntax is slightly different. Now it's time for you to shine. For a homework assignment, we recommend you model some real-world relationships with R and R6. For example, you could model cars. Every A8 is Audi, but not every Audi is an Audi A8, nor is every car an Audi. But every Audi is a car. Give it a go, and make sure to share your results with us on Twitter - <a href="https://twitter.com/appsilon" target="_blank" rel="noopener">@appsilon</a>. We'd love to see what you come up with. Also, if you want to see how object-oriented programming applies to R Shiny, look no further: <ul><li><a href="https://appsilon.com/super-solutions-for-shiny-apps-using-r6-classes/" target="_blank" rel="noopener">Super Solutions for Shiny Apps #4: Using R6 Classes</a></li><li><a href="https://appsilon.com/is-it-possible-to-build-a-video-game-in-r-shiny/" target="_blank" rel="noopener">How to Build a Video Game in R Shiny with CSS, JavaScript, and R6 Classes</a></li></ul>

Contact us!
Damian's Avatar
Damian Rodziewicz
Head of Sales
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
object-oriented programming