On Wed, 1 Sep 2021 05:35:03 -0400 Duncan Murdoch <murdoch.duncan at 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. Thanks!!! cheers, Rolf -- Honorary Research Fellow Department of Statistics University of Auckland Phone: +64-9-373-7599 ext. 88276
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 <r.turner at auckland.ac.nz> wrote:> > On Wed, 1 Sep 2021 05:35:03 -0400 > Duncan Murdoch <murdoch.duncan at 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. > > Thanks!!! > > cheers, > > Rolf > > -- > Honorary Research Fellow > Department of Statistics > University of Auckland > Phone: +64-9-373-7599 ext. 88276 > > ______________________________________________ > R-help at 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]]
On 01/09/2021 6:29 p.m., Rolf Turner wrote:> > On Wed, 1 Sep 2021 05:35:03 -0400 > Duncan Murdoch <murdoch.duncan at 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