Description
It would be nice if partial()
were augmented to comprehend quasiquotation, in a way that:
- preserves the API—maintains the current behavior and argument signature
- enables partialized arguments to be unquoted and spliced
(Additionally, in the degenerate case where no partialized arguments are supplied, partial()
should simply apply as_mapper()
.)
One possibility is as follows:
library(nofrills)
partial <- function(...f, ..., .env = parent.frame(), .lazy = TRUE, .first = TRUE) {
...f <- as_mapper(...f)
args <- if (.lazy) exprs(...) else lapply(quos(...), eval_tidy)
if (is_empty(args))
return(...f)
env <- child_env(.env, ...f = ...f)
if (.first)
nofrills::fn(... ~ ...f(!!! args, ...), ..env = env)
else
nofrills::fn(... ~ ...f(..., !!! args), ..env = env)
}
Remarks:
-
nofrills::fn()
is a simple quasiquotation-enabled function constructor. Though its use is not strictly necessary, it facilitates a very succinct definition ofpartial()
. -
This version of
partial()
is compatible with byte-compiled functions (a difficulty I'd overlooked in partial() fails for functions that introspect context #349). -
Issues Argument matching in partial() doesn't happen for primitive functions #360, option to force evaluation of supplied function in partial to avoid infinite recursion #387 are addressed.
Some examples showing quasiquotation and compatibility with the current behavior:
# Partialized arguments are lazily evaluated, by default
f <- partial(runif, n = rpois(1, 5))
f
#> function (...)
#> ...f(n = rpois(1, 5), ...)
#> <environment: 0x10c5338d8>
f() #> [1] 0.5433265
f() #> [1] 0.9367817 0.7762598
# Eager evaluation by setting .lazy = FALSE
f <- partial(runif, n = rpois(1, 5), .lazy = FALSE)
f
#> function (...)
#> ...f(n = 3L, ...)
#> <environment: 0x10c594ae0>
f() #> [1] 0.9508948 0.8669053 0.4992270
f() #> [1] 0.04073824 0.93821289 0.43544412
# Eager evaluation by unquoting
partial(runif, n = !! rpois(1, 5))
#> function (...)
#> ...f(n = 4L, ...)
#> <environment: 0x10c5b5aa8>
# Arguments can be spliced eagerly
partial(runif, !!! list(n = rpois(1, 5), max = 2))
#> function (...)
#> ...f(n = 3L, max = 2, ...)
#> <environment: 0x10c64ef48>
# Arguments can be spliced lazily
partial(runif, !!! quos(n = rpois(1, 5), max = 2))
#> function (...)
#> ...f(n = rpois(1, 5), max = 2, ...)
#> <environment: 0x10c670e30>
(For now, I'm omitting a better print method to focus on the main point.)