Function that take their main arguments via ... are great for interactive use, but sometimes a bit inconvenient when you want to do stuff in a batch manner.
Interactive mode:
library(settings)
x <- options_manager(a = 1, b = 2)
x()
## $a
## [1] 1
##
## $b
## [1] 2
Batch mode:
Suppose that values has been created by another upstream function and that you would like to feed that to options_manager.
values <- list(a = 1, b = 2)
x <- options_manager(values)
x()
## [[1]]
## [[1]]$a
## [1] 1
##
## [[1]]$b
## [1] 2
Possibly not quite the value we would expect.
The key is to implement a function that handles values passed via ....
Let’s start out with a simple example:
foo <- function(...) {
values <- list(...)
nms <- names(values)
if (is.null(nms)) {
## --> wrapped into list for batch setting
do.call(options_manager, unlist(values, recursive = FALSE))
} else if (any(idx <- nms == "")) {
## --> mixed
values <- c(values[!idx], unlist(values[idx], recursive = FALSE))
do.call(options_manager, values[sort(names(values))])
} else {
## --> regular
options_manager(...)
}
}
That gives you the desired result no matter in what format you provide your input to settings::options_manager
opts <- foo(a = 1, b = 2)
opts()
## $a
## [1] 1
##
## $b
## [1] 2
opts <- foo(list(a = 1, b = 2))
opts()
## $a
## [1] 1
##
## $b
## [1] 2
opts <- foo(list(a = 1), b = 2)
opts()
## $a
## [1] 1
##
## $b
## [1] 2
This draft is a bit more generic.
handleThreedots takes care of transforming values that came in via ... so they are in the typical format that downstream functions taking their main input via ... would expect.
withHandledThreedots is simply a generic wrapper that combines calls to handleThreedots and the actual target function - which would be settings::options_manager in our example case.
handleThreedots <- function(...) {
values <- list(...)
nms <- names(values)
if (is.null(nms)) {
## --> wrapped into list for batch setting
unlist(values, recursive = FALSE)
} else if (any(idx <- nms == "")) {
## --> mixed
c(values[!idx], unlist(values[idx], recursive = FALSE))
## --> note that I did not introduce any sorting as before
## in order to not slow things down additionally
} else {
## --> regular
values
}
}
withHandledThreedots <- function(..., fun) {
do.call(fun, handleThreedots(...))
}
Examples with only handleThreedots
handleThreedots(a = 1, b = 2, c = 3, d = 4)
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
##
## $d
## [1] 4
handleThreedots(list(a = 1, b = 2), list(c = 3, d = 4))
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
##
## $d
## [1] 4
handleThreedots(list(a = 1), b = 2, list(c = 3), d = 4)
## $b
## [1] 2
##
## $d
## [1] 4
##
## $a
## [1] 1
##
## $c
## [1] 3
Actual example:
res <- withHandledThreedots(a = 1, b = 2, c = 3, d = 4,
fun = options_manager)
res()
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
##
## $d
## [1] 4
res <- withHandledThreedots(list(a = 1, b = 2), list(c = 3, d = 4),
fun = options_manager)
res()
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
##
## $d
## [1] 4
res <- withHandledThreedots(list(a = 1), b = 2, list(c = 3), d = 4,
fun = options_manager)
res()
## $b
## [1] 2
##
## $d
## [1] 4
##
## $a
## [1] 1
##
## $c
## [1] 3
This approach can also handle additional arguments that fun might take:
res <- withHandledThreedots(a = 1, b = 2, c = 3, d = 4,
fun = options_manager, .allowed = list(a = inrange(1, 2)))
res()
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
##
## $d
## [1] 4
try(res(a = 3))
res <- withHandledThreedots(list(a = 1, b = 2), list(c = 3, d = 4),
fun = options_manager, .allowed = list(a = inrange(1, 2)))
res()
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
##
## $d
## [1] 4
try(res(a = 3))
res <- withHandledThreedots(list(a = 1), b = 2, list(c = 3), d = 4,
fun = options_manager, .allowed = list(a = inrange(1, 2)))
res()
## $b
## [1] 2
##
## $d
## [1] 4
##
## $a
## [1] 1
##
## $c
## [1] 3
try(res(a = 3))
Just to make sure that the additional argument .allowed of settings::options_manager was simply not shown because its name starts with a dot
foo <- function(..., special = FALSE) {
if (special) {
message("hello world!")
}
list(...)
}
withHandledThreedots(a = 1, b = 2, c = 3, d = 4, fun = foo)
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
##
## $d
## [1] 4
withHandledThreedots(a = 1, b = 2, c = 3, d = 4, fun = foo, special = TRUE)
## hello world!
## $a
## [1] 1
##
## $b
## [1] 2
##
## $c
## [1] 3
##
## $d
## [1] 4