[R] splitting up optional args

Prof Brian Ripley
Sun, 11 Feb 2001 23:38:49 -0000

If you look at some examples, I think that you will see that quite often
NULL is used to indicate your DEFAULT.

Also, missingness is passed down in R but not S, so

foo <- function(x, control)
{
   if(missing(control)) cat("using default control\n")
}
plotfoo <- function(x, foocontrol) { foo(x, control=foocontrol)}

> plotfoo(1:10)
using default control

does work, although you have to use an explicit test for missingness.


The more common version is

foo <- function(x, control=NULL)
{
   if(is.null(control)) cat("using default control\n")
}
plotfoo <- function(x, foocontrol=NULL) { foo(x, control=foocontrol)}

I have yet to see an example in which these did not suffice, and I've been
doing this for more than a decade.


On Sun, 11 Feb 2001, Jason Eisner wrote:

> A question (& possible suggestion) about function calls.
>
> Is there an R idiom to eliminate the redundancy in the
> following common situation?
>
>   foo <- function(x, control=ComplicatedDefault) { etc. }
>
>   plotfoo <- function(x, foocontrol=ComplicatedDefault, ...) {
>                y <- foo(x, control=foocontrol)
>                lines(x,y,...) }
>
> The idea is that there are MANY optional parameters to plotfoo().
> Any specified value for foocontrol should be passed to foo(), and the
> other options should be passed to lines() via "...".
>
> The main trouble above is that the same default for foo()'s control
> parameter has to be specified in two places.  This makes it hard to
> maintain the code correctly.
>
> Further trouble with the above example becomes clear if we elaborate
> it a bit:
>
>   plotfoo <- function(x, foocontrol=ComplicatedDefault, ...) {
>                y <- foo(x, control=foocontrol)
>                z <- foo(x+1, control=foocontrol)
>                lines(x,y,...)    # respect lines()'s defaults
>                par(col=3)        # change lines()'s defaults
>                lines(x,z,...)    # respect new defaults
>                points(x,y+z,...) }  # respect points()'s defaults
>
> When we call plotfoo(x), ComplicatedDefault is evaluated in plotfoo's
> environment rather than foo's, and it is only evaluated once even
> though we call foo twice.  This is not the same as if plotfoo simply
> didn't specify a control parameter when calling foo.  It also
> contrasts with the behavior of the unnamed optional arguments picked
> up by "...".  For example, the default for the graphical
parameter col
> is evaluated in the environment of the callee (lines or points) and is
> evaluated separately for each callee and on each call.
>
> To eliminate the redundancy, I think one could write:
>
>   plotfoo <- function(x, foocontrol=eval(formals(foo)$control), ...)
{etc}
>
> This is not terrible, but it is obscure for such a common situation,
> and not mentioned in the documentation.  And it does not solve
> the evaluation problems mentioned above.
>
> Would it therefore make sense to extend R to allow the following?
>
>   plotfoo <- function(x, foocontrol=, ...) {   # currently illegal
> 	y <- foo(x, control=foocontrol)
>                 plot(x,y,...) }
>
> The rationale is that one can already call foo(x, control=) to request
> the default control argument for foo.  Notionally, there is a special
> "empty" value here that says "give me the default."  So
it would be
> nice to be able to pass that empty value down the call stack.
>
> The user presumably shouldn't be able to do much with this empty value
> except to pass it as a parameter.  Indeed the empty value is
> appropriately analogous to "..." in that it has such restricted
use.
>
> I do recognize that since the user can inspect (and construct?) code,
> an expression for the empty value would be accessible as
> formals(plotfoo)$foocontrol or as substitute(foocontrol) evaluated
> within plotfoo(x).
>
> But this expression could be unevaluable, just as formals(plotfoo)$x
> is unevaluable.  (Of course these two unevaluable expressions should
> be different, so that the user of formals() can see that x is a
> required argument and foocontrol isn't.)
>
> An alternative is to make this expression evaluable.  Suppose we say
> that it evaluates to a special object DEFAULT.  Then presumably
> there would be no way to distinguish between
>    foo(x, control=DEFAULT)
>    foo(x, control=)
>    foo(x)
> but that seems okay to me.  An advantage to this approach is that
> the user could write potentially useful expressions like
>    foo(x, control=ifelse(x==0, 3, DEFAULT))
> and plotfoo could perhaps test its argument to find out whether
> it was DEFAULT, i.e., not specified by the user.
>
> By the way, another use for the proposed extension is as follows:
>   plotfoo <- function(x, col=, lty=) { lines(x,foo(x),col=col,lty=lty) }
> This is simply a version of
>   plotfoo <- function(x, ...) { lines(x,foo(x), ...) }
> that restricts the caller to certain named parameters, for safety.
>
> I am an R novice and have undoubtedly have missed some considerations
> here, so would be interested in any response from the experts.  Thanks!
>
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
> r-help mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
> Send "info", "help", or "[un]subscribe"
> (in the "body", not the subject !)  To: r-help-request at
stat.math.ethz.ch
>
_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._
>

--
Brian D. Ripley,                  ripley at stats.ox.ac.uk
Professor of Applied Statistics,  http://www.stats.ox.ac.uk/~ripley/
University of Oxford,             Tel:  +44 1865 272861 (self)
1 South Parks Road,                     +44 1865 272860 (secr)
Oxford OX1 3TG, UK                Fax:  +44 1865 272595