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

2021-09-02 Thread Rolf Turner


On Wed, 1 Sep 2021 19:29:32 -0400
Duncan Murdoch  wrote:



> 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

Well I *don't* have such code anywhere, but a user could have such a
formula saved in the global environment.

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

Yes.  I have come to realise that and have moved away from the S3
classes and method approach. I now have a solution with which I am
basically satisfied.  But I now understand the problem that you raised.
(Sorry to be so slow!  And thank you for the explanation.)

We need to guard against the possibility that a user may invoke the
"non-formula" syntax, foo(x,y,data)  where x is the predictor and y is
the response, and inadvertently trigger the formula syntax because
there is a pre-constructed formula, with the same name as x, hanging
about.

Not really very likely, but certainly not impossible.

I think that the following works:  suppose that x turns out (using
your handy-dandy try() trick) to be a formula.

x1 <-try(x,silent=TRUE)

If inherits(x1,"formula") firstly check whether this formula exists in
the global environment:

nmx <- deparse(substitute(x))
if(exists(nmx,envir=.GlobalEnv)) {
(throw an error)
}

I have also added an argument forceFormula=FALSE, which if set to TRUE
prevents the error from being thrown.   Just in case using the formula
named by x *really is* what the user wants to do!

I've tested this out a bit (in my real application) and it seems to
work.  I'm sure that there are other pitfalls and Traps for Young
Players.  E.g. someone might call my function from inside
another function in which the offending formula is constructed.
So the offending formula *won't* be found in the global environment and
the error won't be triggered.  Psigh! Somebody will always be able to
find a way to break things. See fortunes::fortune(15).

However I think the code that I have written is reasonably robust, and
does what I want.  (BTW I want the function to accommodate the
"non-formula" syntax, as well as the formula syntax, to maintain some
semblance of backwards-compatibility.)

Thanks again for (a) the try() trick, and (b) pointing out the lurking
danger.

cheers,

Rolf

-- 
Honorary Research Fellow
Department of Statistics
University of Auckland
Phone: +64-9-373-7599 ext. 88276

__
R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.


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

2021-09-01 Thread Duncan Murdoch

On 01/09/2021 6:29 p.m., Rolf Turner wrote:


On Wed, 1 Sep 2021 05:35:03 -0400
Duncan Murdoch  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

__
R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.


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

2021-09-01 Thread Bill Dunlap
Is this the kind of thing you are looking for?  It separates the scoping
issue from the method dispatch by defining another S3-generic function,
".foo".

> foo <- function(x, ..., data=NULL) with(data, .foo(x, ...))
> .foo <- function(x, ...) UseMethod(".foo")
> .foo.default <- function(x, ...) cat("default method\n")
> .foo.integer <- function(x, ...) cat("integer method\n")
> .foo.formula <- function(x, ...) cat("formula method\n")
>
> rm(x)
Warning message:
In rm(x) : object 'x' not found
> foo(32L)
integer method
> foo(y~x)
formula method
> foo(x, data=list(x=2.7))
default method
> x <- 45L ; foo(x)
integer method
> x <- 45L ; foo(x, data=list(x=3.4))
default method
> x <- 45L ; foo(x, data=list(x=Y~X1+X2))
formula method

On Wed, Sep 1, 2021 at 3:30 PM Rolf Turner  wrote:

>
> On Wed, 1 Sep 2021 05:35:03 -0400
> Duncan Murdoch  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.
>
> Thanks!!!
>
> cheers,
>
> Rolf
>
> --
> Honorary Research Fellow
> Department of Statistics
> University of Auckland
> Phone: +64-9-373-7599 ext. 88276
>
> __
> R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide
> http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
>

[[alternative HTML version deleted]]

__
R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.


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

2021-09-01 Thread Rolf Turner


On Wed, 1 Sep 2021 05:35:03 -0400
Duncan Murdoch  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.

Thanks!!!

cheers,

Rolf

-- 
Honorary Research Fellow
Department of Statistics
University of Auckland
Phone: +64-9-373-7599 ext. 88276

__
R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.


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

2021-09-01 Thread Duncan Murdoch

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!

Duncan

__
R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.


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

2021-08-31 Thread Rolf Turner


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

Thanks for any enlightenment.

cheers,

Rolf Turner

-- 
Honorary Research Fellow
Department of Statistics
University of Auckland
Phone: +64-9-373-7599 ext. 88276

__
R-help@r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.