R62S3 started as a single function, R62S3
, and has since grown into two new functions called R62S4
and R62Fun
however instead of changing the original name, it remains as a legacy to the single function where it all began (also because it requires less pleading with CRAN).
R62S3 was created as a way to automatically generate S3 generics and dispatch methods from an R6ClassGenerator. This allows the user to build a fully R6 object-oriented programming interface alongside familiar S3 dispatch, but without the need to code hundreds of lines for each S3 method.
Unsurprisingly, R62S4 is an analogous function that creates S4 generics and methods instead of S3 ones.
R62Fun creates functions that aren’t necessarily dispatch methods. Additional arguments are used to specify what should happen if one of these functions could mask others or if generics exist.
There are a few choices to make when deciding between which of these to implement, they mainly come down to when the function will be called, if the resulting assignments need to be documented and if generics are genuinely useful. We break down these decisions below to help you decide.
In deciding which of the R62
functions to use, the first consideration is when will it be called. For example will you be implementing it directly into your package (as below), which means that your package is shipped with the S3 methods, or will you direct the user to the function so that they can call it only if they choose. If you are giving the user the flexibility to choose, then you can also give them the option to choose any of three, however in this case only R62Fun
should be required as the user can assign all methods to .GlobalEnv
and will be able to see the names of all functions easily.
This section assumes that you are building a package to be exported to CRAN and that you are using roxygen for your documentation. In this case, roxygen requires quite strict conventions for function exporting. We have found that in practice it is next to impossible to dynamically assign S3 generics and methods to the package environment and successfully document the generics/methods in a way that allows I) correct documentation and II) the correct methods exported. S4 generics don’t have the same problem as S4 methods are automatically set to be in the same namespace as S4 generics and thus exported together. R62Fun
doesn’t have this problem either as functions are dynamically assigned to the package namespace and exporting these functions can be done by documenting on a NULL
. For example, if you are using R62Fun
on a class with the function printer
then you document the function that will be created as follows:
In choosing between the three functions, a big consideration is whether creating a new generic function is actually useful. All of these methods define the dispatch method (or function) in the same way, they all take the name of the function and pass it to the corresponding R6 class, so internally they look identical. The power of dispatch lies in the fact that methods with the same name can look the same externally but act differently internally, but in this case they all act the same internally! Therefore you should consider if creating a generic is useful. A generic is useful if you can answer the following two questions positively:
R62S3
or R62S4
otherwise R62Fun
should suffice.If you are using a R62
function in a package that will be exported and shared, I recommend creating a zzz.R
file and adding the function there. This can either be done for specified classes:
Or for every R6 class:
lapply(ls(name=parent.env(environment())),function(x){
if(inherits(get(x),"R6ClassGenerator"))
R62S3::R62S3(get(x),assignEnvir = topenv())
})
To see R62S3
in action, check out the distr6 package (Sonabend and Kiraly, 2019). In distr6
we use the R62Fun
functions in our zzz.R
file to generate S3 methods from specified R6 classes. This package also demonstrates how to document these functions.