This is how I have been thinking that ellipsis works in nested functions: when you hand a set of arguments to a function via ellipsis, any function subsidiary to it on the call stack can get at those arguments -- I thought, via its own ellipsis. I have believed that arguments passed to ellipsis cumulate, so that the inmost ellipsis contains all the arguments passed through ellipsis arguments in any of the functions superior to it in the call stack.
But I just did an experiment to confirm this, and it now appears to me to be wrong. Thus:
> f02 <- function(...){
+ vv <- list(...)
+ print(vv)
+ }
> f01 <- function(...){
+ f02(b = 2)
+ }
> f01(a=1)
$`b`
[1] 2
Here the inner ellipsis does not seem to have inherited the a=1
argument from the outer ellipsis.
So my current theory is that when you take an action that asks for the contents of …
, such as list(…)
, match.call(expand.dots=TRUE)
, or as.list(substitute(list(...)))[-1]
, you only get the first instance of …
that is encountered, based on the search path under normal scoping rules. But I have to say, this seems unlikely to me. If it were true, then, e.g., graphical parameters supplied to a plotting function several calls down would suffer mysterious failures if one of the intervening functions had a …
argument.
So I am wondering if there are some special rules for scoping arguments sought in dot-dot-dots, such as looking for a superior instance if the local one is empty, or if you look in …
for a particular named argument, say list(...)$my_parameter
, and do not find it there . Neither of these solutions strikes me as very plausible, but, well, none of the ones I have come up with do.
Previous questions on this topic seem to be focused mainly on various edge cases. I'm looking more for understanding the passing rules in the normal case (but possibly with multiple layers of calls).