tidyverse / purrr

A functional programming toolkit for R
https://purrr.tidyverse.org/
Other
1.28k stars 272 forks source link

Feature request: `Vectorize()` alternative #1079

Closed lhdjung closed 1 year ago

lhdjung commented 1 year ago

I wonder if this might be within scope for purrr – sorry if not.

base::Vectorize() has some shortcomings:

  1. Its output functions simplify their own output by default if they are closures.
  2. Functions with no arguments, or with as their only argument, are silently returned unchanged. With closures, perhaps this should be an error since they really don't have any arguments to vectorize over. With primitives, any non-dots arguments don't count even if they behave as such. Therefore, if FUN is a primitive with one or more non-dots arguments, it might be preferable to consistently return a vectorized closure that wraps the input. Other primitives should probably be errors. Returning the input function unchanged is a problem especially for those primitives that are not vectorized by themselves:
vectorized_length <- Vectorize(length)
identical(length, vectorized_length)
#> [1] TRUE
# Here, the user may expect `c(a = 10L, b = 26L, c = 1L)`:
vectorized_length(list(a = 1:10, b = letters, c = TRUE))
#> [1] 3

Created on 2023-05-05 with reprex v2.0.2

  1. (Assuming FUN is a closure with at least one non-dots argument:) The body of each output function is identical, and it's quite hard to read. This may be confusing, especially because the input function never visibly appears in the output function. This also means it's not useful to copy and paste the printed output function into one's source code.
  2. The arguments are suboptimally named. vectorize.args might be renamed to over, as in "function f() is vectorized over its argument x".
zenggyu commented 1 year ago

I also vote for adding this feature for purrr. And in addition to the shortcomings @lhdjung suggested, base::Vectorize() also lacks the option to add a progress bar to the resulting function, which I think can be useful.

hadley commented 1 year ago

I think this is out of scope for purrr, since it's relatively straight forward to do by hand:

vectorized_length <- function(x) {
  map_int(x, length)
}

And defining a function that has enough parameters to vectorise a function in every way that someone might want it is going to be hard.