Preventing argument use in R
It sounds silly, but sometimes you don’t want to let people use some arguments of a function. The canonical example is
write.csv. The function is effectively a wrapper to
write.table, but using “,” as the separator and “.” as the decimal.
We could implement the function in a really simple way, as
simple.write.csv <- function(...) write.table(sep = ",", dec = ".", ...)
Under most circumstances, this implementation works as expected. The problem occurs when we pass the
dec arguments into the function; in this case we get a rather unhelpful error message.
Error in write.table(sep = ",", dec = ".", ...) :
formal argument "sep" matched by multiple actual arguments
To get round this, the real implementation of
write.csv is a little more complex. The basic idea behind it is this:
“Take a look at the call to the function
write.csv. Warn the user if any forbidden arguments were passed in, then replace them with specified values. Then pass these on to
Let’s take a look at the code.
write.csv <- function (...)
Call <- match.call(expand.dots = TRUE)
for (argname in c("append", "col.names", "sep", "dec", "qmethod"))
warning(gettextf("attempt to set '%s' ignored", argname), domain = NA)
rn <- eval.parent(Call$row.names)
Call$append <- NULL
Call$col.names <- if (is.logical(rn) && !rn) TRUE else NA
Call$sep <- ","
Call$dec <- "."
Call$qmethod <- "double"
Call[[1L]] <- as.name("write.table")
Understanding this requires some technical knowledge of the R language. When you type things at the R command prompt and hit Enter, two things happen. Those characters are turned into a call, and then they are evaluated to give you your answer. Effectively, what happens is
eval(quote(the stuff you typed))
You can examine the contents of a call by converting it to be a list, for example
The first element of the list gives the function being called, and the rest of the elements are the arguments to pass in to that function. Notice that even ‘+’ is a function (and so is ‘<-‘).
match.call is like calling
quote(the stuff you typed when you called this function)
Returning to the example, let’s have a sample call to
dfr <- data.frame(x = 1:5, y = runif(5))
write.csv(dfr, file = "test.csv", sep = "!")
The crux of understanding
write.csv is in the variable
Call, assigned on the first line of the function.
The rest of the function involves manipulating this object. The element
sep is replaced with a comma (with a warning) and some additional arguments are set. The function that is called is then swapped for
write.table, and finally this call is evaluated.
Deep understanding of this can get a bit mind-melting but if you want to use this principle in your own functions, you can more or less copy and paste from