I am developing an R package which contains over a hundred page-long functions grouped into 10 different themes.
I would like the user to be able to take such themes into consideration when calling functions.
For example, instead of calling foo()
, the user would have to do either
load(theme1) # or whatever would be used to load a subgroup of functions
foo()
or something like
theme1$foo()
What I want to prevent the user from doing is directly loading the package and calling the function without taking the theme into consideration, ie library(package); foo()
library(package); foo()
.
I have used the modules package to accomplish the latter solution in a previous package , but this time around the published version of that package doesn't work very well due to how my functions are interdependent (I've opened a GitHub issue on the topic).
I also thought about coding my own solution, maybe something simple involving the creationg of a sublibrary()
function that exports only a few functions at a time, but I didn't get anywhere with that.
Of course, I guess publishing 10 different packages would also technically work, but I think that's too clumsy a solution to consider.
Apart from using modules, is there a way to implement either of the functionalities above?
Object oriented programming could be used here where theme1
is a class or an object instantiated from the class and the functions associated with it are the methods. There are a number of alternatives here including R reference classes (more info via ?ReferenceClasses
), R6 package , proto package , the R.oo package and function scoping (more info via demo("scoping")
.
For example using the proto package:
library(proto)
theme1 <- proto(
foo = function(.) print("foo"),
bar = function(.) print("bar")
)
theme1$foo()
## [1] "foo"
theme1$bar()
## [1] "bar"
If it is just a matter of grouping and if you don't need local storage or inheritance then we could even use a list:
theme1 <- list(foo = function() print("foo"),
bar = function() print("bar"))
theme1$foo()
## [1] "foo"
theme1$bar()
## [1] "bar"
A number of other OO approaches could be used but they involve creating a class which is then used to create a single object (which is an extra step); however, they do support the $ syntax of the question.
Using R6 we have:
library(R6)
Theme1 = R6Class("theme1",
public = list(
foo = function() print("foo"),
bar = function() print("bar")
)
)
theme1 <- Theme1$new() # generate a Theme1 object
theme1$foo()
## [1] "foo"
theme1$bar()
## [1] "bar"
or using Reference Classes (no packages needed):
setRefClass(
"Theme1",
methods = list(
foo = function() print("foo"),
bar = function() print("bar")
)
)
theme1 <- Theme1() # generate Theme1 object
theme1$foo()
## [1] "foo"
theme1$bar()
## [1] "bar"
or using function scoping (no packages needed):
Theme1 <- function() list(
foo = function() print("foo"),
bar = function() print("bar")
)
theme1 <- Theme1()
theme1$foo()
## [1] "foo"
theme1$bar()
## [1] "bar"
Here's an idea of how you could achieve the sublibrary
function. In this case we will use it to load a small subsection of ggplot2
sublibrary <- function(theme) {
subgroups <-
list(basic = list(ggplot = ggplot2::ggplot,
aes = ggplot2::aes,
geom_line = ggplot2::geom_line,
geom_point = ggplot2::geom_point),
advanced = list(scale_color_manual = ggplot2::scale_color_manual,
theme = ggplot2::theme))
attach(list2env(subgroups[[theme]]))
}
This means that without loading an entire package I could do:
sublibrary("basic")
ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
geom_point()
In reality, the subgroup
object would be stored outside the function, in a list or environment within the package namespace.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.