"Correct" way to specifiy optional arguments in R functions

These are my rules of thumb:

If default values can be calculated from other parameters, use default expressions as in:

fun <- function(x,levels=levels(x)){
    blah blah blah
}

if otherwise using missing

fun <- function(x,levels){
    if(missing(levels)){
        [calculate levels here]
    }
    blah blah blah
}

In the rare case that you thing a user may want to specify a default value that lasts an entire R session, use getOption

fun <- function(x,y=getOption('fun.y','initialDefault')){# or getOption('pkg.fun.y',defaultValue)
    blah blah blah
}

If some parameters apply depending on the class of the first argument, use an S3 generic:

fun <- function(...)
    UseMethod(...)


fun.character <- function(x,y,z){# y and z only apply when x is character
   blah blah blah 
}

fun.numeric <- function(x,a,b){# a and b only apply when x is numeric
   blah blah blah 
}

fun.default <- function(x,m,n){# otherwise arguments m and n apply
   blah blah blah 
}

Use ... only when you are passing additional parameters on to another function

cat0 <- function(...)
    cat(...,sep = '')

Finally, if you do choose the use ... without passing the dots onto another function, warn the user that your function is ignoring any unused parameters since it can be very confusing otherwise:

fun <- (x,...){
    params <- list(...)
    optionalParamNames <- letters
    unusedParams <- setdiff(names(params),optionalParamNames)
    if(length(unusedParams))
        stop('unused parameters',paste(unusedParams,collapse = ', '))
   blah blah blah 
}

To be honest I like the OP's first way of actually starting it with a NULL value and then checking it with is.null (primarily because it is very simply and easy to understand). It maybe depends on the way people are used to coding but the Hadley seems to support the is.null way too:

From Hadley's book "Advanced-R" Chapter 6, Functions, p.84 (for the online version check here):

You can determine if an argument was supplied or not with the missing() function.

i <- function(a, b) {
  c(missing(a), missing(b))
}
i()
#> [1] TRUE TRUE
i(a = 1)
#> [1] FALSE  TRUE
i(b = 2)
#> [1]  TRUE FALSE
i(1, 2)
#> [1] FALSE FALSE

Sometimes you want to add a non-trivial default value, which might take several lines of code to compute. Instead of inserting that code in the function definition, you could use missing() to conditionally compute it if needed. However, this makes it hard to know which arguments are required and which are optional without carefully reading the documentation. Instead, I usually set the default value to NULL and use is.null() to check if the argument was supplied.


You could also use missing() to test whether or not the argument y was supplied:

fooBar <- function(x,y){
    if(missing(y)) {
        x
    } else {
        x + y
    }
}

fooBar(3,1.5)
# [1] 4.5
fooBar(3)
# [1] 3

Tags:

Function

R