[How-to] Share content between several R6 instances

The object oriented system is a nice and powerful way to program and to build softwares and data in R. Yet, it can be a little bit daunting at first, especially if you’ve alway been coding in R, and with functions. In this blogpost, we’ll have a look at {R6}, one of the most downloaded package from the CRAN, which is one of the backbone of a lot of package backends, and specifically answer the question of data sharing across class instances.

What is {R6}?

In a few words, {R6} is a modern and flexible Object Oriented (“OO”) framework for R.

So yes, your next question might be: what does “object oriented” mean? With OO, you’re creating objects, and inside these objects are data and methods (i.e, function). Running these methods can change the content of the data contained inside the object.

As in any OO system, {R6} has a system of classes and instances of these classes — which is the subject of this post: how can you share content between every instances of a class?

When should we need {R6}?

One main use case is when you need to build an object, and you want to enclose inside this object information, but also perform a series of actions on these information. You could do this by defining functions and objects and use all these objects together. But the idea here is to have something that is enclosed inside the same object, and all the objects from the same class starts with the same content and methods.

R6 can be used to deal with database connection, for example. Inside the R6 object, you could find all the information about the connection, and also methods specifically related to interacting with the database. You can also built documents, which are constructed inside the object.

So this is what I do here: on the named list given as a parameter, I imap a custom function that will create, for each elements of the list, a id : param; element.

Then, this is put inside a style { content }, and as I need to use { as a real character, I’m using % to provide an opening and closing character (as {glue} uses { to mark the element to unquote by default, and this can be overridden).

This one is used to view the content of the page inside your default browser. I made the concat a private method (it concatenates the elements), HTML turns characters into HTML, and html_print shows the content.

And finally, our private list. I chose to put concat in here as I didn’t want nor need this function to be accessible outside the object, I could also have put it outside the class.

Several pages, one CSS

It would be better if every instance of the class shared the same CSS, right?

So, here’s the thing: once an object is instantiated, its public objects are sealed: if A and B are instances of the C class, changing something in the public list of A won’t change it in B. And if you change something in C, that won’t “roll down” to the instances of the class. Which is a behavior you could expect: if I change the title of A, I don’t want the title of B to be changed.

But in our case, we need to be able to access the same data from multiple instances: in other word, I want all my instances of WebPages to access the very same CSS code. To do this, we’ll use R environments. In the private field of our R6 class definition, we’ll set up an environment in the `shared` field. Then, everytime I will create a new object of this class, the shared variable will point to the very same environment (and in fine, to the same CSS content).

Here, once plop_1<\code> is initialized, it contains the variable a with a specific value. If I initiate plop_2 and change_a<\code>, the only value that is changed is the one inside plop_2. But when it comes to b, as shared is an environment — and that environment is copied by reference, not by value —, we can share data across all instances.

Conceptually speaking, each instance of the a variable points to its own space in memory, while all the shared elements point to the same spot, hence contain the same value.