tidyverse / purrr

A functional programming toolkit for R

Home Page:https://purrr.tidyverse.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature request: `Vectorize()` alternative

lhdjung opened this issue · comments

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".

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.

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.