c(...)
collects the parameters passed to the function into a vector. If you don't, and just wrote rank(...)
(ignoring the negation for a second), R will splice each parameter into the call, calling
rank(a = 0.2117986, b = 0.4388764, c = 0.4204525)
#> Error in rank(a = 0.2117986, b = 0.4388764, c = 0.4204525): unused arguments (a = 0.2117986, b = 0.4388764, c = 0.4204525)
which fails, as there aren't such parameters in rank
. Even unnamed, the values won't work nicely for the first three parameters that do exist:
rank(0.2117986, 0.4388764, 0.4204525)
#> Error in match.arg(ties.method): 'arg' must be NULL or a character vector
Instead, c
collects the dots into a vector, which is passed to the first x
parameter of rank
, akin to
rank(c(a = 0.2117986, b = 0.4388764, c = 0.4204525))
#> a b c
#> 1 3 2
Why that result has to be coerced to a list for bind_rows
to work is unclear. It's true that it's rare to have a vector that should be row-bound into a data frame (usually the parameters are data frames or lists so they can handle multiple types), but it's entirely possible, as this example shows. In base R, rbind
handles the cases identically, returning matrices:
rbind(c(a = 1, b = 2, c = 3),
c(a = 4, b = 5, c = 6))
#> a b c
#> [1,] 1 2 3
#> [2,] 4 5 6
rbind(list(a = 1, b = 2, c = 3),
list(a = 4, b = 5, c = 6))
#> a b c
#> [1,] 1 2 3
#> [2,] 4 5 6
Given that bind_rows
—like all dplyr verbs—always returns a data frame, I'm not sure there's a rationale for not handling this case.