44

In another question, sapply(substitute(...()), as.character) was used inside a function to obtain the names passed to the function. The as.character part sounds fine, but what on earth does ...() do?

It's not valid code outside of substitute:

> test <- function(...) ...()
> test(T,F)
Error in test(T, F) : could not find function "..."

Some more test cases:

> test <- function(...) substitute(...())
> test(T,F)
[[1]]
T

[[2]]
F

> test <- function(...) substitute(...)
> test(T,F)
T
Community
  • 1
  • 1
Ari B. Friedman
  • 71,271
  • 35
  • 175
  • 235
  • Not sure it answers the question, but Here is an [R help link](https://mailman.stat.ethz.ch/pipermail/r-help/2012-July/319252.html) in which Bill Dunlap is using this to create a function from the `...` element. Hence `...()` creates a function for each element of `...` – mnel Sep 21 '12 at 03:19
  • Yeah that's where I picked it up but that doesn't really get at how it works I don't think. – Tyler Rinker Sep 21 '12 at 03:37
  • `list(...)` will evaluate the elements of `...`, while `substitute(...())` will return them unevaluated. But for the life of me I can't work out why. (I'm lost trying to find where this is in the source code) – mnel Sep 21 '12 at 04:18
  • 1
    Among many other things, I'm curious why `substitute(...)` only returns the first element of the of `...`. (Note that `test <- function(...) substitute({...}); test(T, F)` will return them both, in a fashion. – Josh O'Brien Sep 21 '12 at 06:02
  • 2
    @mnel, if you're better in c than I am (not a difficult achievement), have a look in the file coerce.c in */src/main for the functions `substitute`, `substituteList` and `do_substitute`. They do deal with ellipses in a special way. – BenBarnes Sep 21 '12 at 06:28
  • 2
    @mnel I don't think `...()` creates a function for element of `...`. `...` is a pairlist and the first argument of `substitute()` is supposed to be an expression. In an expression `(` can be used to simply group things. As all `substitute` is doing is returning the parse tree for `...`, I wonder if the `()` is just a trick of the parser or similar to get it to group the `...` somehow. As Bill's email shows, there isn't any function creation as that would show up in the resulting parse tree would it not? – Gavin Simpson Sep 21 '12 at 08:01
  • 4
    My strong recommendation is not to use this construct. ever. Bill D. may have discovered a great sneaky trick, but given that it's pretty much undocumented there's really no guarantee it will work in all cases or that some future mod to the core of R won't break it. Sometimes we can be too clever for our own good :-( . I'll retract this comment if Patrick "Inferno" Burns can show otherwise. :-) – Carl Witthoft Sep 21 '12 at 11:31
  • 1
    That goes for `substitute(...[])`, too... – Matthew Plourde Sep 21 '12 at 12:38
  • @mplourde `test <- function(...) substitute(...[])` `test(T,F)` `T[F, ]` My mind just exploded. – Ari B. Friedman Sep 21 '12 at 12:52
  • @AriB.Friedman `Rgames> foo<-test(3,"four",5) Rgames> typeof(foo) [1] "language" Rgames> foo 3["four", 5, ]` So there :-) – Carl Witthoft Sep 21 '12 at 14:25

1 Answers1

27

Here's a sketch of why ...() works the way it does. I'll fill in with more details and references later, but this touches on the key points.

  1. Before performing substitution on any of its components, substitute() first parses an R statement.

  2. ...() parses to a call object, whereas ... parses to a name object.

  3. ... is a special object, intended only to be used in function calls. As a consequence, the C code that implements substitution takes special measures to handle ... when it is found in a call object. Similar precautions are not taken when ... occurs as a symbol. (The relevant code is in the functions do_substitute, substitute, and substituteList (especially the latter two) in R_SRCDIR/src/main/coerce.c.)

So, the role of the () in ...() is to cause the statement to be parsed as a call (aka language) object, so that substitution will return the fully expanded value of the dots. It may seem surprising that ... gets substituted for even when it's on the outside of the (), but: (a) calls are stored internally as list-like objects and (b) the relevant C code seems to make no distinction between the first element of that list and the subsequent ones.


Just a side note: for examining behavior of substitute or the classes of various objects, I find it useful to set up a little sandbox, like this:

f <- function(...) browser()
f(a = 4, 77, B = "char")
## Then play around within the browser
class(quote(...))  ## quote() parses without substituting
class(quote(...()))
substitute({...})
substitute(...(..., X, ...))
substitute(2 <- (makes * list(no - sense))(...))
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
  • 1
    The one bit I still don't understand is how the substitution for `...` in `substitute(...())` changes the class of the returned object. All other substitutions within call objects that I've examined (e.g. `substitute(j())`, `substitute(j(...))`, or even `j <- pairlist(x=4, y=5); substitute(j())`) return call objects. I think the answer is down at the C level of calls to `CDR`, `CAR`, `SETCDR` and `CONS`, which I'm just not able to fully follow/game out. – Josh O'Brien Sep 22 '12 at 16:10
  • 2
    Continuing on this weird path, try this: `substitute((...))` and then this: `as.list(substitute((...)))`. It seems that the result in both cases is an "improper" `call` object to the function `'('`, since this function only accepts one argument. In the first case, `print` assumes it's a proper call, and shows only the first argument. In the second case, we can see all the elements of the call. – Ferdinand.kraft Apr 16 '13 at 18:32