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:
@name printer
@title Printer Method
....@export
NULL
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: 1. The generic will be re-used by a non-R62S3/4
generated method somewhere else in the package 1. The generic will be
re-used outside of the package (perhaps by being called from a different
package) If you can answer ‘yes’ to both of these then use
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:
::R62S3(yourR6Class, assignEnvir = topenv())
R62S3}
Or for every R6 class:
lapply(ls(name=parent.env(environment())),function(x){
if(inherits(get(x),"R6ClassGenerator"))
::R62S3(get(x),assignEnvir = topenv())
R62S3 })
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.
Install from CRAN with
install.packages("R62S3")
Or the latest stable build from GitHub with
::install_github("RaphaelS1/R62S3") remotes
Dr Franz Kiraly for initial discussions about the idea.
Prof. Dr. Peter Ruckdeschel for conversations that lead to a
simplification in the code structure in earlier versions.