How "..." Behaves in R

So what does ... mean anyway?

The R documentation has precious little to say about the ... construct. Here I'll try to show how what it does, how it works, and how the fexpr package enhances your ability to do things with it.

What ... means in a function header?

Here is an example function definition:

function(x, y, ...) {
  with(x, doTheThing(y, ...))
}

The function header declares three arguments: x, y, and .... Of these, ... is treated specially.

Whenever a function call is made, the R interpreter matches up the argument list and against the defined arguments of a function. If the argument list in the header contains ..., any arguments that don't match the other named arguments get bundled up in .... Otherwise, non-matching arguments cause an error.

There are equivalent mechanisms in other languages. For example in Python, our function might be specified as follows:

```{python, exec=FALSE} def myFun(x, y, *args, kwargs): with(x, doTheThing(y, *args), kwargs)

Here `*args` and `**kwargs` are playing the role that `...` plays in
R. Any named arguments that don't match get put into the dict
`kwargs`; excess unnamed arguments get put into the list `args`.
One difference is that Python keeps "named" and "unnamed" 
arguments separate while R mixes both kinds into `...`

### What does `...` mean in function code?

If `...` appears in the list of arguments to call a function, it is
treated specially by the interpreter. The arguments saved in `...` are unpacked,
and those arguments are pasted into the argument list for the function.
Note that `list` does not know that its arguments came out of a `...`.

This behavior is a lot like "argument interpolation" in Python, where
you use the 

### Where do the arguments named `...` live?

In a variable local to the function, named `...`!

```r
function(x, y, ...) {

}

Note that ... is not bound to any value when it is empty. The R interpreter checks this condition and

Because ... is a reserved word, just writing ... in code will not show you the value of that variable. but you can (in current R) access it using get().

EXAMPLE

What kind value is bound to ...?

What goes in ... is a special type of object, in the R souece it is called a DOTSXP. Which is very similar to a "pairlist," except that it has a different type. Further Each element of the DOTSXP is a promise (called PROMSXP in the R code).

R always expects a DOTSXP to be assigned to the ... name. Any other type will cause an error. There is no user-level interface to create a DOTSXP (but the point of fexpr is to provide such functions.)

A promise is what is created for each argument that is passed to a function, and supports R's lazy evaluation of function arguments. A promise consists of three parts: an expression, an environment to evaluate it in, and the value (after it has been evaluated)

In this case, we see from 'substitute' that the original expressions are preserved through the

Relation between ... and lazy evaluation

Every element in ... is a promise (with its , and each argument to a function becomes a promise

This has a very important consequence; it means that the environment of your argument might not be that of your caller, if your caller used ... to hand down arguments. For this reason, the common pattern of using parent.frame or match.call in conjunction with eval to perform non-standard evaluation is fragile; for the correct patterns see the vignette XXX.

How arguments get put into ...

M-



crowding/nse documentation built on Jan. 5, 2024, 12:14 a.m.