# The defaults of a function argument are not binded

## 2018/04/12

When writing a function in R, it’s good to provide commonly appropriate default values for the arguments so that the user could call the function conviniently in most cases. However, if you happen to have some functions sharing the same default values, you may be lured to create a variable (e.g., opts) and assign those default values to this variable.

The code looks good, runs well, works fine. Until one day, the little opts gets redefined somewhere…

opts <- c("a", "b")
your_fun <- function(x  = opts) { x }
your_fun()
# "a" "b"
opts <- c("b", "c")
your_fun()
# "b" "c"


If you defined the opts inside of an R package and didn’t export it, you would find that your code works great under devtools::load_all() but fails ruthlessly after the package being built and reloaded, complaining can’t find opts.

UPDATE Thanks Yihui for pointing out my mistake. It will complain only when you use match.arg(x) (by omitting the param choices). Except this, normally it should be safe to use inside of a package, because the function will only look up the defaults variable at the environment where it’s defined.

I create a simple example to demostrate this.

test1 <- local({
opts <- c("a", "b", "c")
function(x = opts) print(x)
})
test2 <- local({
opts <- c("a", "b", "c")
function(x = opts) print(match.arg(x))
})
test3 <- local({
function(x = c("a", "b", "c")) print(match.arg(x))
})
test1()
#> [1] "a" "b" "c"
test2() # match.arg() will try to find the opts in the calling frame so fails
#> Error in eval(formal.args[[as.character(substitute(arg))]]): object 'opts' not found
test3()
#> [1] "a"
opts <- c("d", "e", "f")
test1() # finds opt in the envir where test1() gets defined
#> [1] "a" "b" "c"
test2()
#> Error in match.arg(x) : 'arg' must be of length 1
test2("d")
#> "d"


So here’s the suggestion:

• Be attention to use a variable as the default value if the working envir is the same as your function’s defining envir, because your variable may get changed unintentionally.
• Normally it’s ok to use in a package as the plain default value. However, if you are providing a set of opts with match.arg(), you should write it like:

your_fun <- function(x) {
x <- match.arg(x, choices = opts)
}


your_fun <- function(x = opts) {