On the mechanics of function evaluation and argument matching
I agree that failing fast is a good principle. My initial point led the other way though, i.e. any unmatched formal arguments without default values should be handled in one of two ways: 1. Fail the function call. This is what most non-functional languages do e.g. Python
def f(x,y,z): x
...
f(2)
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f() takes exactly 3 arguments (1 given) 2. Perform partial application, like some functional languages e.g. Haskell f :: Int -> Int -> Int -> Int f x y z = x *Main> let a = f 2 *Main> :t a a :: Int -> Int -> Int Otherwise if an argument is truly optional, I don't see why a default value cannot be assigned to the formal argument when defining the function (excepting the edge cases you pointed out earlier). Brian
On Jul 17, 2013, at 2:35 PM, Peter Meilstrup <peter.meilstrup at gmail.com> wrote:
On Wed, Jul 17, 2013 at 10:20 AM, Ben Bolker <bbolker at gmail.com> wrote:
Brian Rowe <rowe <at> muxspace.com> writes:
Thanks for the lead. Given the example in ?missing though,
wouldn't it be safer to explicitly define a
default value of NULL:
myplot <- function(x, y=NULL) {
if(is.null(y)) {
y <- x
x <- 1:length(y)
}
plot(x, y)
}
[snip] In my opinion the missing() functionality can indeed be fragile (for example, I don't know how I can manipulate an existing call to make an argument be 'missing' when it was previously 'non-empty')
Like so:
thecall <- quote(x[i,j]) thecall[[3]] <- quote(expr=) thecall
x[, j]
and using an explicit NULL is often a good idea. This makes the documentation a tiny bit less wieldy if you have lots of parameters ...
I could certainly imagine a variant of R in which missing and NULL are unified, and NULL is the default for any binding that exists but was not given a value. I would probably prefer it on the grounds of being smaller and more consistent. (At the C implementation level, R_MissingArg and R_NilValue are just two parallel uses of the null object pattern with different behavior, which is a bit silly) But one advantage the missing value behavior can have is that it "fails early", i.e. it generates an error closer to where a function wants to use a value it was not provided, rather than "failing late," where a NULL propagates though your data and you have to do more debugging work to find out where it came from. This kind of fragility can be a good thing as it's easier to debug problems that happen closer to the point of failure. For instance,
myplot <- function(y, x=1:length(y)) plot(x,y) myplot()
Error in plot(x, y) (from #1) : error in evaluating the argument 'x' in selecting a method for function 'plot': Error in length(y) (from #1) : 'y' is missing I didn't think about what myplot should do with no arguments. As it turns out it is an error, as R refuses to pass a missing value along to length() or plot(), which is reasonable. Compare with a default-NULL version.
myplot <- function(y=NULL, x=1:length(y)) plot(x,y) myplot()
Instead of failing early and generating a stack trace pointing you at the problem, myplot() now generates a graph with points at (0,0) and (1,1) -- most surprising! This is because R happily forwards NULL to length() and plot() where it refused to earlier. In more complicated code nulls can pass along several layers before causing problems, making those problems more of a headache to debug. Peter [[alternative HTML version deleted]]
______________________________________________ R-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel