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