[Rd] On the mechanics of function evaluation and argument matching

Brian Rowe rowe at muxspace.com
Wed Jul 17 21:14:13 CEST 2013

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).


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

More information about the R-devel mailing list