...
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.
...
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 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
...
and lazy evaluationEvery 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
.
...
M-
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.