/extendedRef

Extensions to the reference class system

Primary LanguageR

extendedRef - Extended Reference Classes

Build Status codecov.io lifecycle

The goal of staticRef is to add static and private variable and methods support for R reference classes.

Installation

You can install the released version of extendedRef from CRAN with:

install.packages("extendedRef")

Example

To setup an extended reference class use the setExtendedRefClass() function. For this example we will create a registered counter class that tracks all the counters created and the individual counts.

counter <- 
    setExtendedRefClass('registeredCounter',
        # fields define the publicly accessible fields, same as setRefClass
        fields = c(name= 'character'),
        
        # private are the variables that are not accessible via the `$` operator
        private = c(count = 'integer'),
        
        # staic  specifies static variables common to all variables.
        static = c( counters = 'list' ),
        
        # static.const specifies constants for the class that cannot be changed.
        # since these do not change they are specified once by a list with the 
        # values they should take.
        static.const  = list( max = 3L),
        # for illustration, we are artificially setting a maximum of 3 counters 
        
        # Methods are specified with lists of functions.
        # static methods
        static.methods = list( how_many_counters = function()length(counters)
                             , add_counter = function(counter){
                                 x <- list(counter)
                                 names(x) <- counter$name
                                 counters <<- c(counters, x)
                             }),

        # private methods
        private.methods = list(private_initialize = function(){
            # The public initialize, cannot use or alter private variables,
            # the `private_initialize` function serves this role. 
            count <<- 0L

            # The private initialize can also access the static variables,
            if (how_many_counters() < max) add_counter(.self) else
                stop("Cannot create another counter!")
        }),

        # methods as with setRefClass defines the public methods.
        methods = list( increment = function()count <<- count + 1L
                      , get_count = function()count
                      )
        # note that the public function alters a 'private' variable.
        )

This class may not be very useful but illustrates the concepts.

As before the counter object is a object generator function that creates objects from the exteneded reference class 'registeredCounter'. However, the generator object also provides a method for accessing the static variables and methods.

counter$static$how_many_counters()
#> [1] 0
counter$static$max
#> [1] 3
first <- counter(name = 'first')

We now have first as a counter and we have one counter registered.

first$get_count()
#> [1] 0
counter$static$how_many_counters()
#> [1] 1

Counter is a reference class so you can do all the things that you would do with a reference class

first$increment()
first$get_count()
#> [1] 1
also.first <- first
also.first$get_count()
#> [1] 1

Note that one thing you cannot do is access the private variable count.

first$count
#> Error in envRefInferField(x, what, getClass(class(x)), selfEnv): 'count' is not a valid field or method name for reference class "registeredCounter"

For the static variables all object have access to them from their internal methods, but not publicly.

first$how_many_counters()
#> Error in envRefInferField(x, what, getClass(class(x)), selfEnv): 'how_many_counters' is not a valid field or method name for reference class "registeredCounter"

Let's create a few more counters.

second <- counter(name = 'second')
third <- counter(name = 'third')
fourth <- counter(name = 'fourth')
#> Error in private.methods$private_initialize(): Cannot create another counter!

We cannot add the fourth counter because we specified that we can only have 3 total.

counter$static$how_many_counters()
#> [1] 3

Since the counters are registered we can retrieve them from the registry.

counter$static$counters[['first']]$get_count()
#> [1] 1

The static variables are tied to the stored definition so there is always only one copy.

validate_that(identical(counter$def, getClass('registeredCounter')))
#> [1] TRUE