1#' Invoke functions. 2#' 3#' @keywords internal 4#' @description 5#' 6#' \Sexpr[results=rd, stage=render]{purrr:::lifecycle("retired")} 7#' 8#' This pair of functions make it easier to combine a function and list 9#' of parameters to get a result. `invoke` is a wrapper around 10#' `do.call` that makes it easy to use in a pipe. `invoke_map` 11#' makes it easier to call lists of functions with lists of parameters. 12#' 13#' @param .f For `invoke`, a function; for `invoke_map` a 14#' list of functions. 15#' @param .x For `invoke`, an argument-list; for `invoke_map` a 16#' list of argument-lists the same length as `.f` (or length 1). 17#' The default argument, `list(NULL)`, will be recycled to the 18#' same length as `.f`, and will call each function with no 19#' arguments (apart from any supplied in `...`. 20#' @param ... Additional arguments passed to each function. 21#' @param .env Environment in which [do.call()] should 22#' evaluate a constructed expression. This only matters if you pass 23#' as `.f` the name of a function rather than its value, or as 24#' `.x` symbols of objects rather than their values. 25#' @section Life cycle: 26#' 27#' These functions are retired in favour of [exec()]. They are no 28#' longer under active development but we will maintain them in the 29#' package undefinitely. 30#' 31#' * `invoke()` is retired in favour of the simpler `exec()` function 32#' reexported from rlang. `exec()` evaluates a function call built 33#' from its inputs and supports tidy dots: 34#' 35#' ``` 36#' # Before: 37#' invoke(mean, list(na.rm = TRUE), x = 1:10) 38#' 39#' # After 40#' exec(mean, 1:10, !!!list(na.rm = TRUE)) 41#' ``` 42#' 43#' * `invoke_map()` is is retired without replacement because it is 44#' more complex to understand than the corresponding code using 45#' `map()`, `map2()` and `exec()`: 46#' 47#' ``` 48#' # Before: 49#' invoke_map(fns, list(args)) 50#' invoke_map(fns, list(args1, args2)) 51#' 52#' # After: 53#' map(fns, exec, !!!args) 54#' map2(fns, list(args1, args2), function(fn, args) exec(fn, !!!args)) 55#' ``` 56#' 57#' @family map variants 58#' @examples 59#' # Invoke a function with a list of arguments 60#' invoke(runif, list(n = 10)) 61#' # Invoke a function with named arguments 62#' invoke(runif, n = 10) 63#' 64#' # Combine the two: 65#' invoke(paste, list("01a", "01b"), sep = "-") 66#' # That's more natural as part of a pipeline: 67#' list("01a", "01b") %>% 68#' invoke(paste, ., sep = "-") 69#' 70#' # Invoke a list of functions, each with different arguments 71#' invoke_map(list(runif, rnorm), list(list(n = 10), list(n = 5))) 72#' # Or with the same inputs: 73#' invoke_map(list(runif, rnorm), list(list(n = 5))) 74#' invoke_map(list(runif, rnorm), n = 5) 75#' # Or the same function with different inputs: 76#' invoke_map("runif", list(list(n = 5), list(n = 10))) 77#' 78#' # Or as a pipeline 79#' list(m1 = mean, m2 = median) %>% invoke_map(x = rcauchy(100)) 80#' list(m1 = mean, m2 = median) %>% invoke_map_dbl(x = rcauchy(100)) 81#' 82#' # Note that you can also match by position by explicitly omitting `.x`. 83#' # This can be useful when the argument names of the functions are not 84#' # identical 85#' list(m1 = mean, m2 = median) %>% 86#' invoke_map(, rcauchy(100)) 87#' 88#' # If you have pairs of function name and arguments, it's natural 89#' # to store them in a data frame. Here we use a tibble because 90#' # it has better support for list-columns 91#' if (rlang::is_installed("tibble")) { 92#' df <- tibble::tibble( 93#' f = c("runif", "rpois", "rnorm"), 94#' params = list( 95#' list(n = 10), 96#' list(n = 5, lambda = 10), 97#' list(n = 10, mean = -3, sd = 10) 98#' ) 99#' ) 100#' df 101#' invoke_map(df$f, df$params) 102#' } 103#' @export 104invoke <- function(.f, .x = NULL, ..., .env = NULL) { 105 .env <- .env %||% parent.frame() 106 args <- c(as.list(.x), list(...)) 107 do.call(.f, args, envir = .env) 108} 109 110as_invoke_function <- function(f) { 111 if (is.function(f)) { 112 list(f) 113 } else { 114 f 115 } 116} 117 118#' @rdname invoke 119#' @export 120invoke_map <- function(.f, .x = list(NULL), ..., .env = NULL) { 121 .env <- .env %||% parent.frame() 122 .f <- as_invoke_function(.f) 123 map2(.f, .x, invoke, ..., .env = .env) 124} 125#' @rdname invoke 126#' @export 127invoke_map_lgl <- function(.f, .x = list(NULL), ..., .env = NULL) { 128 .env <- .env %||% parent.frame() 129 .f <- as_invoke_function(.f) 130 map2_lgl(.f, .x, invoke, ..., .env = .env) 131} 132#' @rdname invoke 133#' @export 134invoke_map_int <- function(.f, .x = list(NULL), ..., .env = NULL) { 135 .env <- .env %||% parent.frame() 136 .f <- as_invoke_function(.f) 137 map2_int(.f, .x, invoke, ..., .env = .env) 138} 139#' @rdname invoke 140#' @export 141invoke_map_dbl <- function(.f, .x = list(NULL), ..., .env = NULL) { 142 .env <- .env %||% parent.frame() 143 .f <- as_invoke_function(.f) 144 map2_dbl(.f, .x, invoke, ..., .env = .env) 145} 146#' @rdname invoke 147#' @export 148invoke_map_chr <- function(.f, .x = list(NULL), ..., .env = NULL) { 149 .env <- .env %||% parent.frame() 150 .f <- as_invoke_function(.f) 151 map2_chr(.f, .x, invoke, ..., .env = .env) 152} 153#' @rdname invoke 154#' @export 155invoke_map_raw <- function(.f, .x = list(NULL), ..., .env = NULL) { 156 .env <- .env %||% parent.frame() 157 .f <- as_invoke_function(.f) 158 map2_raw(.f, .x, invoke, ..., .env = .env) 159} 160 161#' @rdname invoke 162#' @export 163invoke_map_dfr <- function(.f, .x = list(NULL), ..., .env = NULL) { 164 .env <- .env %||% parent.frame() 165 .f <- as_invoke_function(.f) 166 map2_dfr(.f, .x, invoke, ..., .env = .env) 167} 168#' @rdname invoke 169#' @export 170invoke_map_dfc <- function(.f, .x = list(NULL), ..., .env = NULL) { 171 .env <- .env %||% parent.frame() 172 .f <- as_invoke_function(.f) 173 map2_dfc(.f, .x, invoke, ..., .env = .env) 174} 175#' @rdname invoke 176#' @export 177#' @usage NULL 178invoke_map_df <- invoke_map_dfr 179 180#' @rdname invoke 181#' @export 182#' @usage NULL 183map_call <- function(.x, .f, ...) { 184 .Defunct("`map_call()` is deprecated. Please use `invoke()` instead.") 185} 186