[R] What if there's nothing to dispatch on?

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Thu Sep 2 01:29:32 CEST 2021


On 01/09/2021 6:29 p.m., Rolf Turner wrote:
> 
> On Wed, 1 Sep 2021 05:35:03 -0400
> Duncan Murdoch <murdoch.duncan using gmail.com> wrote:
> 
>> On 31/08/2021 11:59 p.m., Rolf Turner wrote:
>>>
>>> I'm trying to build a pair of (S3) methods, a "formula" method and a
>>> "default" method.  The methods have a "data" argument.  If the
>>> variables in question cannot be found in "data" then they should be
>>> sought in the global environment.
>>>
>>> My problem is that the generic dispatches on its first argument,
>>> which may be a formula (in which case it of course dispatches to
>>> the formula method) or the first of the variables.  If this
>>> variable exists in the global environment then all is well.  But if
>>> it doesn't exist there, then the generic falls over with an error
>>> of the form "object 'x' not found" --- because there isn't anything
>>> to dispatch on.
>>>
>>> I'd *like* to be able to tell the generic that if "x" is not found
>>> then it should dispatch to the default method (which will, if the
>>> call is sensible, find "x" in "data").
>>>
>>> Is there any way to tell the generic to do this?
>>>
>>> Or is there any other way out of this dilemma? (Other than "Give up
>>> and go to the pub", which I cannot currently do since Auckland is
>>> in Level 4 lockdown. :-) )
>>>
>>
>> That design is probably not a good idea:  what if one of the
>> variables in data matches the name of some other object in the global
>> environment? Then it would dispatch on that other object, and things
>> won't go well.
>>
>> But here's a way to shoot yourself in the foot:
>>
>> function(x) {
>>     x1 <- try(x, silent = TRUE)
>>     if (inherits(x1, "try-error"))
>>       foo.default(x)
>>     else
>>       UseMethod("foo", x)
>> }
>>
>> Happy shooting!
> 
> Thanks Duncan. I don't understand your warning, but.
> 
> If I call foo(y ~ x,data=xxx) I want the generic to dispatch to the
> formula method.  That method will then look for y and x first in xxx,
> and if it can't find them there it then will look for them in the global
> environment.
> 
> If I call foo(x,y,data=xxx) I want the generic to dispatch to the
> default method, irrespective of whether x exists in the global
> environment.  I can't figure out how to arrange this.  As before
> (if I could arrange for the dispatch to happen as desired) I would want
> the method to look for y and x first in xxx, and if it can't find them
> there it then will look for them in the global environment.
> 
> It doesn't matter there is an "x" in both xxx and in the global
> environment; the methods will/should use the "x" from xxx.
> 
> I don't see a problem with respect to this issue.
> 
> Whatever.  I can't get your shoot-in-the-foot solution to work anyway.
> 
> If I set
> 
>      xxx <- data.frame(u=1:10,v=rnorm(10))
> 
> and do
> 
>      foo(x=u,y=v,data=xxx)
> 
> I get
> 
>> Error in foo.default(x, y, data) : Cannot find x.
> 
> The argument names need to match up.  Note that calling foo.default()
> directly works:
> 
>      foo.default(x=u,y=v,data=xxx)
> 
> runs just fine.
> 
> I think I'm going to have to give up on the classes-and-methods
> approach.  I *think* I can see a way through with a using a single
> function and if-statements based on your "try" idea.
> 

I don't know the header of your foo() method, but let's suppose foo() is

   foo <- function(x, data, ...) {
     UseMethod("foo")
   }

with

   foo.formula <- function(x, data, ...) {
     # do something with the formula x
   }

   foo.default <- function(x, data, ...) {
     # do the default thing.
   }

Now you have

   xxx <- data.frame(u = 1:10, v = rnorm(10))
   foo(x = u, y = v, data = xxx)

You want this to dispatch to the default method, because u is not a 
formula, it's a column in xxx.  But how do you know that?  Maybe in some 
other part of your code you have

   u <- someresponse ~ somepredictor

So now u *is* a formula, and this will dispatch to the formula method, 
causing havoc.

I think Bill's suggestion doesn't help here.  To do what you want to do 
doesn't really match what S3 is designed to do.

Duncan



More information about the R-help mailing list