To date when writing R functions I\'ve passed undefined arguments as NULL values and then tested whether they are NULL i.e.
f1 <- function (x = NULL) {
missing(x) seems to be a bit faster than using default arg to x equal to NULL.
> require('microbenchmark')
> f1 <- function(x=NULL) is.null(x)
> f2 <- function(x) missing(x)
> microbenchmark(f1(1), f2(1))
Unit: nanoseconds
expr min lq median uq max neval
f1(1) 615 631 647.5 800.5 3024 100
f2(1) 497 511 567.0 755.5 7916 100
> microbenchmark(f1(), f2())
Unit: nanoseconds
expr min lq median uq max neval
f1() 589 619 627 745.5 3561 100
f2() 437 448 463 479.0 2869 100
Note that in the f1 case x is still reported as missing if you make a call f1(), but it has a value that may be read within f1.
The second case is more general than the first one. missing() just means that the user did not pass any value. is.null() (with NULL default arg) states that the user either did not pass anything or he/she passed NULL.
By the way, plot.default() and chisq.test() use NULL for their second arguments. On the other hand, getS3method('t.test', 'default') uses NULL for y argument and missing() for mu (in order to be prepared for many usage scenarios).
I think that some R users will prefer f1-type functions, especially when working with the *apply family:
sapply(list(1, NULL, 2, NULL), f1)
Achieving that in the f2 case is not so straightforward.