Why does Map only require one set of vectors as arguments, but mapply requires both that and the MoreArgs argument?

Viewed 231

As far as I am aware, aside from simplification, everything that can be done with mapply can be done with Map. After all, Map is a wrapper for mapply. However, I was surprised to see that mapply takes both a ... set of arguments (which the docs call "arguments to vectorize over (vectors or lists of strictly positive length, or all of zero length)") and a MoreArgs argument on top of the required function f, whereas Map does not use MoreArgs, only needing ... (which the docs just call "vectors") and f.

My question is this: Why does mapply need MoreArgs but Map doesn't? Can mapply do something that Map cannot? Or is mapply trying to make something easier that would be harder with Map? And if so, what?

I suspect that sapply may be a useful point of reference for an answer. It may be helpful to compare its X, FUN and ... arguments to mapply's ... and MoreArgs.

1 Answers

Let's look at the code of Map :

Map
function (f, ...) 
{
    f <- match.fun(f)
    mapply(FUN = f, ..., SIMPLIFY = FALSE)
}

As you wrote, it's just a wrapper, and the dots are forwarded to mapply, note that SIMPLIFY is hardcoded to FALSE

Why does mapply need MoreArgs but Map doesn't?

It's a design choice, possibly due in part to historical reasons, I wouldn't have minded explicit MoreArgs and USE.NAMES arguments (or a SIMPLIFY = TRUE argument for that matter), but I believe the rationale is that Map is meant to be simple, if you want to tweak parameters you're encouraged to use mapply. Nevertheless, you can use MoreArgs and USE.NAMES with Map, they will travel through the dots to the mapply call, though it is undocumented, as the doc describes the ... argument as "vectors".

Map(sum, setNames(1:2, c("a", "b")), 1:2)
#> $a
#> [1] 2
#> 
#> $b
#> [1] 4

Map(sum, setNames(1:2, c("a", "b")), 1:2, USE.NAMES = FALSE)
#> [[1]]
#> [1] 2
#> 
#> [[2]]
#> [1] 4

Map(
  replicate, 
  2:3,
  c(FALSE, TRUE),
  MoreArgs = list(expr = quote(runif(1))))
#> [[1]]
#> [[1]][[1]]
#> [1] 0.7523955
#> 
#> [[1]][[2]]
#> [1] 0.4922519
#> 
#> 
#> [[2]]
#> [1] 0.81626690 0.07415023 0.56264388

The equivalent mapply calls would be :

mapply(sum, setNames(1:2, c("a", "b")), 1:2, SIMPLIFY = FALSE)
#> $a
#> [1] 2
#> 
#> $b
#> [1] 4

mapply(sum, setNames(1:2, c("a", "b")), 1:2, USE.NAMES = FALSE, SIMPLIFY = FALSE)
#> [[1]]
#> [1] 2
#> 
#> [[2]]
#> [1] 4

mapply(
  replicate,
  2:3,
  c(FALSE, TRUE),
  MoreArgs = list(expr = quote(runif(1))))
#> [[1]]
#> [[1]][[1]]
#> [1] 0.6690229
#> 
#> [[1]][[2]]
#> [1] 0.7529774
#> 
#> 
#> [[2]]
#> [1] 0.8632736 0.7822639 0.8553680

Can mapply do something that Map cannot? Or is mapply trying to make something easier that would be harder with Map? And if so, what?

You cannot use SIMPLIFY with Map:

Map(sum, 1:3, 1:3, SIMPLIFY = TRUE)
#> Error in mapply(FUN = f, ..., SIMPLIFY = FALSE): formal argument "SIMPLIFY" matched by multiple actual arguments

a bit of history

  • mapply was introduced with R 1.7.0
  • Map was introduced with R 2.6, the NEWS item reads :

New higher-order functions Reduce(), Filter() and Map().

The f argument name is shared among those functions and they are documented on the same page. The reason why moving away from naming functions FUN is unknown to me, but the consistency between these 3 functions (and the other functions documented in ?Reduce) explains why mapply and Map don't name their function argument the same way (the consistency explains the upper case M too I guess).

In the doc we can also read :

Map is a simple wrapper to mapply which does not attempt to simplify the result, similar to Common Lisp's mapcar (with arguments being recycled, however). Future versions may allow some control of the result type.

So, in theory, as I understand from the last sentence, Map could be upgraded to provide some type stability, similar to what vapply does. It seems to me that R-devel didn't go all the way because they wanted to take time to decide properly what to do, and they left it in this state since then.

Related