The splicr
package implements a new pipe operator, %!>%
, which uses
the values of a list on the left as arguments for a right-hand function
call.
library(splicr)
example_fun = function(a, b = "B", c = "C", d = "D") {
paste0("a = ", a, ", b = ", b, ", c = ", c, ", d = ", d)
}
list("A", c = "Something Else") %!>% example_fun(d = "Alt D Value")
#> [1] "a = A, b = B, c = Something Else, d = Alt D Value"
You can install the development version of splicr
with
remotes::install_github("Christopher-Peterson/splicr")
The multi-pipe follows a few simple rules for splicing arguments into a function:
The following lines all produce the same output:
list(a = "A", b = "B") %!>% example_fun
#> [1] "a = A, b = B, c = C, d = D"
list(a = "A", b = "B") %!>% example_fun()
#> [1] "a = A, b = B, c = C, d = D"
Since no arguments were specified on the right hand side, these are
equivalent to do.call()
and similar alternatives.
do.call(example_fun, list(a = "A", b = "B"))
#> [1] "a = A, b = B, c = C, d = D"
rlang::exec(example_fun, a = "A", b = "B")
#> [1] "a = A, b = B, c = C, d = D"
rlang::exec(example_fun, !!!list(a = "A", b = "B"))
#> [1] "a = A, b = B, c = C, d = D"
However, splicr
’s partial argument specification provides greater
flexibility than do.call()
, and using magrittr::%>%
to pipe into
rlang::exec
with the splice operator (!!!
) with has a rather
convoluted syntax:
library(magrittr)
list(a = "A", b = "B") %>% {rlang::exec(.fn = example_fun, !!!.)}
#> [1] "a = A, b = B, c = C, d = D"
If there are unnamed arguments on both the left and right, the left arguments will go first.
list("A", "B") %!>% example_fun("Not A", "Not B")
#> [1] "a = A, b = B, c = Not A, d = Not B"
Unnamed arguments will be placed in the first available position.
list("A", "Not B") %!>% example_fun(b = "B", "Not C")
#> [1] "a = A, b = B, c = Not B, d = Not C"
If there same argument is named on both sides, the right hand version will be used (with a warning, by default).
list("A", b = "Not B") %!>% example_fun(b = "B", "Not C")
#> Warning: Some arguments piped to example_fun were also manually specified.
#> Piped values ignored for these arguments:
#> b
#> [1] "a = A, b = B, c = Not C, d = D"
list(a = 1, b = 2) %!>% { function(a, b) a + b }
#> [1] 3
purrr
-style lambda functions are also supported:
list(1, 2, 3) %!>% { ~ .x + .y + ..3}
#> [1] 6
Not wrapping lambdas or inline functions in braces this causes strange parser errors. The multi-pipe tries to detect when this happens and fail with a useful error message.
Piping to a function factory (a function that returns other functions) will normally splice the arguments into the factory.
factory = function(a = "A") {
function(b, c = "C") paste(a, b, c)
}
out <- list("Hrm?") %!>% factory
# out is a function
list("B", "C") %!>% out
#> [1] "Hrm? B C"
To splice into the factory’s output, wrap the right hand side in parentheses.
list("B") %!>% (factory("Hrm?"))
#> [1] "Hrm? B C"
It’s straightforward to combine %!>%
with %>%
as part of chain:
library(purrr, quietly = TRUE)
#>
#> Attaching package: 'purrr'
#> The following object is masked from 'package:magrittr':
#>
#> set_names
# simulate some random numbers, and see how much
# their range differs among samples
set.seed(2341)
list(.x = rpois(10, lambda = 5), # .x is count
.y = 50 * rexp(10)) %!>% # .y is scale
map2(~runif(n = .x, min = -.y, max = .y)) %>% # random number set
map(range) %>% # range of each set
transpose %!>% # convert to a list of min and max
map2_dbl(~.y - .x)
#> [1] 0.4732697 10.4708108 11.1807544 5.6274485 151.1372094
#> [6] 62.6456384 49.5798816 107.8628834 36.1210788 13.5070311
You can also use the pipe to easily access S3 object elements:
lm(Sepal.Length ~ Sepal.Width, data = iris) %!>% {
function(fitted.values, residuals, ...){
plot(fitted.values, residuals)
invisible(list(...))
}
}