Some experimenting is needed. But I think a subclass is likely to be cleaner. The official model is that methods and generic differ only in the body, so having an object-based way to say that some methods are non-conforming feels more natural to me.
On Jan 29, 2015, at 9:57 AM, Michael Lawrence <lawrence.mich...@gene.com> wrote: > Would we really need the special class or would simply checking the formals > of the method against those of the generic be simple and fast enough? > > On Thu, Jan 29, 2015 at 9:41 AM, John Chambers <j...@r-project.org> wrote: > >> I wouldn't want to add more to the current approach; if someone would like >> to devote some time, the much preferable idea IMO would be to replace the >> whole mechanism. >> >> Here's one suggestion: >> >> 1. have a class, say "nonConformingMethod" for method definitions that >> diverge in the argument list. >> >> 2. the internal dispatch code checks the class of the selected definition >> (this can likely be done with little cost in the standard case). In the >> case of non-conforming, the arguments are rematched to define the method's >> other arguments. >> >> The possibilities need examining, but my feeling is that the re-matching >> should happen in the current frame, as opposed to doing a new call. >> >> There is a fair amount of code, for example in callNextMethod, that >> requires some computations using knowledge of the current mechanism. If at >> some point we required re-installing all packages using non-conforming >> methods, that code could be made simpler and faster. >> >> John >> >> >> >> >> On Jan 29, 2015, at 8:08 AM, William Dunlap <wdun...@tibco.com> wrote: >> >>> I wish it didn't have to depend on the name '.local'. >>> >>> Back when I wrote a lot of S4 methods I avoided the auto-generated .local >>> and named the local function something that made sense so that is was >> easier >>> for a user to track down the source of an error. >>> >>> E.g., define the generic QQQ with numeric and integer methods: >>> setGeneric("QQQ", >>> function(x, ...)NULL) >>> setMethod("QQQ", >>> signature=signature(x="numeric"), >>> function(x, lower, ...) { >>> if (x<lower) stop("x<lower") >>> }) >>> setMethod("QQQ", >>> signature=signature(x="integer"), >>> function(x, ...) { >>> .QQQ.integer <- function(x, lower, ...) if (x<lower) >> stop("x<lower") >>> .QQQ.integer(x, ...) >>> }) >>> and try using them: >>>> QQQ(3.4, 10) >>> Error in .local(x, ...) : x<lower >>>> traceback() >>> 4: stop("x<lower") at #4 >>> 3: .local(x, ...) >>> 2: QQQ(3.4, 10) >>> 1: QQQ(3.4, 10) >>>> QQQ(3L, 10) >>> Error in .QQQ.integer(x, ...) : x<lower >>>> traceback() >>> 4: stop("x<lower") at #4 >>> 3: .QQQ.integer(x, ...) at #5 >>> 2: QQQ(3L, 10) >>> 1: QQQ(3L, 10) >>> I think the latter gives the user more guidance on how to fix the >> problem. >>> >>> Perhaps instead of searching for an assignment to '.local' you could >>> search for an assignment to the name of the function used in the last >>> function call of the method. >>> >>> >>> >>> Bill Dunlap >>> TIBCO Software >>> wdunlap tibco.com >>> >>> On Thu, Jan 29, 2015 at 6:34 AM, Hadley Wickham <h.wick...@gmail.com> >> wrote: >>> On Thu, Jan 29, 2015 at 7:57 AM, John Chambers <j...@r-project.org> >> wrote: >>>> >>>> On Jan 28, 2015, at 6:37 PM, Michael Lawrence < >> lawrence.mich...@gene.com> wrote: >>>> >>>>> At this point I would just due: >>>>> >>>>> formals(body(method)[[2L]]) >>>>> >>>>> At some point we need to figure out what to do with this .local() >> confusion. >>>> >>>> Agreed, definitely. The current hack is to avoid re-matching >> arguments on method dispatch, so a fix would need to be fairly deep in the >> implementation. >>>> >>>> But I don't think the expression above is quite right. >> body(method)[[2L]] is the assignment. You need to evaluate the rhs. >>>> >>>> Here is a function that does the same sort of thing, and returns the >> standard formals for the generic if this method does not have nonstandard >> arguments. We should probably add a version of this function for 3.3.0, so >> user code doesn't have hacks around the current hack. >>>> >>>> methodFormals <- function(f, signature = character()) { >>>> fdef <- getGeneric(f) >>>> method <- selectMethod(fdef, signature) >>>> genFormals <- base::formals(fdef) >>>> b <- body(method) >>>> if(is(b, "{") && is(b[[2]], "<-") && identical(b[[2]][[2]], >> as.name(".local"))) { >>>> local <- eval(b[[2]][[3]]) >>>> if(is.function(local)) >>>> return(formals(local)) >>>> warning("Expected a .local assignment to be a function. >> Corrupted method?") >>>> } >>>> genFormals >>>> } >>> >>> I have similar code in roxygen2: >>> >>> # When a generic has ... and a method adds new arguments, the S4 method >>> # wraps the definition inside another function which has the same >> arguments >>> # as the generic. This function figures out if that's the case, and >> extracts >>> # the original function if so. >>> # >>> # It's based on expression processing based on the structure of the >>> # constructed method which looks like: >>> # >>> # function (x, ...) { >>> # .local <- function (x, ..., y = 7) {} >>> # .local(x, ...) >>> # } >>> extract_method_fun <- function(x) { >>> fun <- x@.Data >>> >>> method_body <- body(fun) >>> if (!is.call(method_body)) return(fun) >>> if (!identical(method_body[[1]], quote(`{`))) return(fun) >>> >>> first_line <- method_body[[2]] >>> if (!is.call(first_line)) return(fun) >>> if (!identical(first_line[[1]], quote(`<-`))) return(fun) >>> if (!identical(first_line[[2]], quote(`.local`))) return(fun) >>> >>> first_line[[3]] >>> } >>> >>> >>> -- >>> http://had.co.nz/ >>> >>> ______________________________________________ >>> R-devel@r-project.org mailing list >>> https://stat.ethz.ch/mailman/listinfo/r-devel >>> >> >> >> [[alternative HTML version deleted]] >> >> ______________________________________________ >> R-devel@r-project.org mailing list >> https://stat.ethz.ch/mailman/listinfo/r-devel >> > > [[alternative HTML version deleted]] > > ______________________________________________ > R-devel@r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel ______________________________________________ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel