It would be nice to have a "friendlier" version of compose() that:
- sets the formals to that of the first called function
- is more consistent with tidyverse semantics (i.e., supports splicing and formula syntax)
- has a more informative print method
- works as a drop-in replacement
To satisfy these desiderata (the second of which I presume is planned), I have something like the following in mind, which is a minor tweaking of the current implementation:
library(rlang)
compose <- function(...) {
fs <- lapply(dots_list(...), as_function)
n <- length(fs)
last <- as_closure(fs[[n]])
`__call_last` <- function() {
call_last <- mut_node_car(sys.call(-1), last)
eval_bare(call_last, parent.frame(2))
}
`__rest` <- rev(fs[-n])
set_attrs(
`formals<-`(
value = formals(last),
function() {
out <- `__call_last`()
for (f in `__rest`)
out <- f(out)
out
}
),
class = "composite_function"
)
}
decompose <- function(x) {
if (!inherits(x, "composite_function"))
abort("Not a composite function")
environment(x)$fs
}
print.composite_function <- function(x, ...) {
cat("<composite_function>\n")
cat("(Listed in calling order)\n")
for (f in rev(decompose(x))) {
cat("\n")
print(f)
}
invisible(x)
}
(The method for formals setting is the same as in #349.)
Example:
foo <- compose(log, ~abs(.) + 1)
foo
#> <composite_function>
#> (Listed in calling order)
#>
#> function (..., .x = ..1, .y = ..2, . = ..1)
#> abs(.) + 1
#>
#> function (x, base = exp(1)) .Primitive("log")
args(foo)
#> function (..., .x = ..1, .y = ..2, . = ..1)
#> NULL
decompose(foo)
#> [[1]]
#> function (x, base = exp(1)) .Primitive("log")
#>
#> [[2]]
#> function (..., .x = ..1, .y = ..2, . = ..1)
#> abs(.x) + 1
It would be nice to have a "friendlier" version of
compose()that:To satisfy these desiderata (the second of which I presume is planned), I have something like the following in mind, which is a minor tweaking of the current implementation:
(The method for formals setting is the same as in #349.)
Example: