[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).
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
More information about the R-devel
mailing list