5

This question is a follow-up to a previous answer which raised a puzzle.

Reproducible example from the previous answer:

Models <- list( lm(runif(10)~rnorm(10)),lm(runif(10)~rnorm(10)),lm(runif(10)~rnorm(10)) )
lm1 <- lm(runif(10)~rnorm(10))
library(functional)
# This works
do.call( Curry(anova, object=lm1), Models )
# But so does this
do.call( anova, Models )

The question is why does do.call(anova, Models) work fine, as @Roland points out?

The signature for anova is anova(object, ...)

anova calls UseMethod, which should* call anova.lm which should call anova.lmlist, whose first line is objects <- list(object, ...), but object doesn't exist in that formulation.

The only thing I can surmise is that do.call might not just fill in ellipses but fills in all arguments without defaults and leaves any extra for the ellipsis to catch? If so, where is that documented, as it's definitely new to me!

* Which is itself a clue--how does UseMethod know to call anova.lm if the first argument is unspecified? There's no anova.list method or anova.default or similar...

Community
  • 1
  • 1
Ari B. Friedman
  • 71,271
  • 35
  • 175
  • 235
  • isn't it the same situation as `do.call(plot, list(1:2, 1:2, col="red"))`? It seems to obey the standard behaviour re positional matching, named arguments and `...`. – baptiste Aug 07 '13 at 16:25
  • I'm confused, what's puzzling about `do.call(anova, Models)` being the same as `anova(Models[[1]], Models[[2]], Models[[3]])`? – eddi Aug 07 '13 at 16:29
  • Because `do.call` is always talked about as a solution to `...`, I guess it's never mentioned that it will also fill in according to the positional matching rules. But this was pretty surprising to me. – Ari B. Friedman Aug 07 '13 at 16:41
  • I really don't understand what positional matching rules you're talking about - can you please explain in a couple of sentences? – eddi Aug 07 '13 at 16:42
  • @eddi the positional matching rule is what baptiste referred to: empty arguments (those without defaults or which don't have named arguments specified) are filled in in order. – Ari B. Friedman Aug 07 '13 at 16:48
  • 1
    @SimonO101 seems like you're missing the point of the "*", rolling back, again... – eddi Aug 07 '13 at 16:55
  • 1
    `do.call` just does a basic lexical substitution from `do.call(f, list(...))` to `f(...)` - and you can always pass both named and unnamed arguments along to ... – hadley Aug 07 '13 at 16:59
  • @hadley Can you explain more? "Basic lexical substitution...to `f(...)`" is similar to what I've read in other places, and is what led me to believe that it substitutes for `...` rather than following the usual positional, naming, unnamed rules. – Ari B. Friedman Aug 08 '13 at 16:16
  • @AriB.Friedman tried to describe it in the answer below - I think you're missing how ... works, not how `do.call` works – hadley Aug 08 '13 at 17:17
  • @hadley I agree that's where the confusion came from. Thanks for the answer. – Ari B. Friedman Aug 08 '13 at 21:20

3 Answers3

5

In a regular function call ... captures arguments by position, partial match and full match:

f <- function(...) g(...)
g <- function(x, y, zabc) c(x = x, y = y, zabc = zabc)

f(1, 2, 3)
# x    y zabc 
# 1    2    3     
f(z = 3, y = 2, 1)
# x    y zabc 
# 1    2    3     

do.call behaves in exactly the same way except instead of supplying the arguments directly to the function, they're stored in a list and do.call takes care of passing them into the function:

do.call(f, list(1, 2, 3))
# x    y zabc 
# 1    2    3     
do.call(f, list(z = 3, y = 2, 1))
# x    y zabc 
# 1    2    3     
hadley
  • 102,019
  • 32
  • 183
  • 245
2

I think it is worth stressing that the names of the list elements do matter. Hadley mentioned it, but it can be an annoyance. Consider the next example:

x <- rnorm(1000)
y <- rnorm(1000)
z <- rnorm(1000) + 0.2

Models <- list()
Models$xy <- lm(z~x)
Models$yz <- lm(z~y)

# This will fail, because do.call will not assign anything to the argument "object" of anova
do.call(anova, Models)
# This won't
do.call(anova, unname(Models))
Antoine Lizée
  • 3,743
  • 1
  • 26
  • 36
1

do.call passes the first element of the list to the first argument:

fun <- function(x,...) {
  print(paste0("x=",x))

  list(x, ...)
}

do.call(fun, list(1,2))
# [1] "x=1"
# [[1]]
# [1] 1
# 
# [[2]]
# [1] 2
Roland
  • 127,288
  • 10
  • 191
  • 288
  • what else could it possibly do?! isn't `do.call(f, list(1,2))` by definition supposed to be equivalent to `f(1,2)`? I'm really not getting the OP. – eddi Aug 07 '13 at 16:46
  • Well, we thought that would be the case for `f <- function(...)`, but not for `f <- function(x ,...)`. I expected the latter to give an error with `do.call`. – Roland Aug 07 '13 at 16:49
  • @eddi I could only substitute in for the `...` and return an error otherwise. It's obviously more useful the way it actually works, but the most common usage is to fill in for the `...`. – Ari B. Friedman Aug 07 '13 at 16:50
  • :) not sure why you'd think that, but ok – eddi Aug 07 '13 at 16:54
  • We were wrong, but thinking that `do.call` would only fill in the ellipses doesn't seem far-fetched to me, though the examples section of the documentation shows otherwise. – Roland Aug 07 '13 at 16:58
  • This answer is wrong! If the elements of the list is named, the statement is false. – Antoine Lizée Nov 08 '13 at 23:17
  • @AntoineLizée The list elements in the question are not named ... You cannot expect an answer to answer something that wasn't asked. – Roland Nov 09 '13 at 16:10
  • I'm not sure to totally agree with that. The spirit of SO is to answer something that is true in general, or make it clear if it is specific to the situation. – Antoine Lizée Nov 12 '13 at 18:56