# Chapter 8 Apendix A4 – rogramming tidbits

## 8.1 The Ellipsis

``````bar = function(x, polynomial = 2){
return(x^polynomial)
}

foo = function(x,...){
z = bar(x,...)
return(z)
}``````
``foo(6)``
``##  36``
``foo(6, polynomial = 3)``
``##  216``
``foo(6, polynomial = 2)``
``##  36``

## 8.2 match.call()

The R documentation states “match.call retunrs a call in which all of the specified arguments are specified by their full names.”

Cool. What?

What this does is takes in a function, in our case the `lm` function and returns an object which is of class `"call"`. This disassembles the specified function from its input arguments, allowing us to manipulate arguments without evaluating them.

Suppose we have a function

``````foo = function(x,y){
bar = match.call()
names = match(c('x', 'y'), names(bar))
args = list(bar, names)
return(args)
}

foo(2,4)``````
``````## []
## foo(x = 2, y = 4)
##
## []
##  2 3``````

We see the output of `match.call` is effective a regurgitation of our input. What `match.call` has done is disassemble our function without evaluating it so we can index into our arguments directly. `match` then allows us to explicitly define the arguments of a function as new variables without evaluating our function.

Agan, this all seems relatively etherial and unnecessary. So, let’s look at a practical example of why this is useful. Say we wanted to write our own function that takes in a user specified formula and outputs a design matrix data frame using the built in `stats::model.frame` function.

``````foo = function(formula, data, weights){
model = stats::model.frame(formula = formula, data = data, weights = weights)
}
foo(y~x, data = data.frame(y = rnorm(10), x = runif(10)))``````
``## Error in model.frame.default(formula = formula, data = data, weights = weights): invalid type (closure) for variable '(weights)'``

You see we get an error here. The reason being that we didn’t specify a default value for our weights input. As a result, R tries to use a built in function `stats::weights` to fill in the missing `weights` argument. For a small function like this it would be trivial to add in `weights = NULL` as a default argument in the inner function, but for large functions with dozens of arguments, this is tedious and unnecessary.

We could circumvent this by using the `match.call` argument.

``````foo = function(formula, data, weights){
mf = match.call() #freeze the function and regurgitate it as a call class
m = match(c('formula', 'data', 'weights'), names(mf),0L) #extract arguments that are relevant for our lower level function.
mf = mf[c(1L, m)]
mf\$drop.unused.levels = TRUE #gets rid of unused arguments
mf[[1L]] = quote(stats::model.frame)#replace 'foo' with 'stats::modelframe'
mf
}

foo(y~x, data = data.frame(y = rnorm(10), x = runif(10)))``````
``````## stats::model.frame(formula = y ~ x, data = data.frame(y = rnorm(10),
##     x = runif(10)), drop.unused.levels = TRUE)``````

So you see our output is now a respecified form of our orginal function. However, also note it has not yet been evaluated. We can add one more line of code to unfreeze time and evaluate the expression.

``````foo = function(formula, data, weights){
mf = match.call() #freeze the function and regurgitate it as a call class
m = match(c('formula', 'data', 'weights'), names(mf),0L) #extract arguments that are relevant for our lower level function.
mf = mf[c(1L, m)]
mf\$drop.unused.levels = TRUE #gets rid of unused arguments
mf[[1L]] = quote(stats::model.frame)#replace 'foo' with 'stats::modelframe'
eval(mf)
}

foo(y~x, data = data.frame(y = rnorm(10), x = runif(10)))``````
``````##             y         x
## 1   0.7077870 0.5346722
## 2   0.5259239 0.8335249
## 3   0.6047109 0.9842342
## 4   0.2054106 0.8383571
## 5   1.2202408 0.4338031
## 6   0.8241745 0.4732692
## 7  -0.7060031 0.8553911
## 8   0.6882589 0.6423501
## 9   1.0299911 0.4453169
## 10  0.5221993 0.8782426``````

As you can see, we have effectively redefined our function without evaluating it. Again, this can be completely ciorcumvented by specifying default arguments, but often this is an unreasonable task for large projects.

You can return from your side quest here

P.S Another benefit of this method is it allows you to specify arguments in your function that have the same name as `.Primitive`s. All-in-all its just another defensive programming strategy that can also be leveraged to convert between functions within other functions.