Gabor Grothendieck wrote: >I think a rule is needed that would require that a class not succeed any of >its parents on the method list. That way "array" could never come >after "vector" and, in general, a parent would never be found prior >to any of its descendents. > > A good idea & it would help, we can certainly do something like this (probably for 2.4, not 2.3). But so long as the inheritance search is greedy, a "wrong" method can still be chosen.
Suppose, say, class A contains B and C, and C contains D. Function foo has methods for B and D. We would agree, I think, that foo(a) where a is from class A should get the B method (parent) rather than the D method (grandparent). But classess B and D are unrelated so there's no obvious ordering. My preference would be for a full search. The ordering you suggest would at least help avoid some branches of the search. If we can manage at some point to re-implement important parts of the methods package, in C and more built-in, that should save much more computing time than the full search would add. >Of course that still does not address the problem that the order >of the setMethod's rather than the relationships among the classes >can influence the search order but it would eliminate the situation >we saw here. > >On 4/13/06, John Chambers <[EMAIL PROTECTED]> wrote: > > >>What a delicious example! (I'm taking the liberty of sharing it with >>r-devel, since it raises some good issues.) >> >>You have two questions, presumably: >> >>1 - how could the order of the setMethod calls make a difference in the >>results? >>2 - what's causing the infinite loop & how could it be avoided, reliably? >> >>Second question first. The danger sign is the "vector" method: >> >> >>setMethod("foo", >> signature(A = "vector"), >> function(A, ...) { >> foo(matrix(A, nrow = 1), ...) >> }) >> >>This handles a vector by recalling the generic with a matrix. But "vector" >>is a superclass of "matrix" (see getClass("matrix")) and you DON'T have an >>explicit method defined for "matrix". So for sure if "vector" was the only >>method, you would be in immediate trouble. Coercing down the inheritance >>tree is potentially bad news. >> >>Generally, if you're going to take over dispatch by coercing an argument & >>then recalling the generic, the result is simplest if you have an exact >>match for the new call, not relying on inheritance. After all, if you're >>doing the work to coerce the argument, might as well pick one that works >>right away. Not a universal rule, but other things being equal ... >> >>There's an additional issue with methods for "matrix" and "array" because R >>never allows 2-way arrays to have class "array", which means using array() >>here with the same dimensions would not have helped. Also, matrix and array >>objects are weird in that they are not basic vectors but also have no class >>attribute, and is.object() is FALSE for them. >> >>More interesting though--how can the order of the setMethod() calls matter? >>To see that we need to look at the methods list object. (The discussion is >>simplified to the case that only one argument is involved, which doesn't >>affect the result.) >> >>The MethodsList object has a slot containing a named list of methods, with >>the names being those of the classes that appeared in the setMethod calls, >>in the order that the calls occurred(note!). All classes are essentially >>equal from the view of the generic function, so there's no ordering favoring >>the "more relevant". >> >>When method dispatch occurs, the code first looks for an exact match to the >>class of the actual argument--that's a quick search in the names of the >>list. >> >>If the direct search fails, the code now looks for an inherited method. The >>key point is that this second search is "greedy"--the first inherited method >>found is used. >> >>NOW it should be clear why the order of the setMethod() calls matters. You >>have two potential inherited methods here for "matrix"; namely, "array" and >>"vector". WE know that the "array" method is `closer', and the R dispatcher >>could decide this also, if it were willing to look through all possible >>inheritance paths and drop one possibility if a better one was found. >> >>It currently doesn't do any further search, and doing so would be a modest >>efficiency hit. I'm inclined to think the cost would be worth it to >>eliminate unpleasant suprises like this one, but opinions may differ. (Once >>found, the inherited method is stored directly in the list used for the >>first lookup, so the hit is only the first time a particular signature >>turns up.) >> >>To make the difference clearer, I added to your example another generic >>"bar" with the same methods as "foo", but with the order of the setMethod >>calls reversed. >>By looking at the "methods" slot in the two cases, we can see why the bad >>("vector") method is selected for bar() but not for foo(): >> >> >> >>>names(getMethods("foo")@methods) >>> >>> >>[1] "array" "vector" >> >> >>>names(getMethods("bar")@methods) >>> >>> >>[1] "vector" "array" >> >>After running foo(1:10) and trying to run bar(1:10): >> >> >> >>>showMethods("foo") >>> >>> >>Function "foo": >> A = "array" >> A = "vector" >> A = "integer" >> (inherited from A = "vector") >> A = "matrix" >> (inherited from A = "array") >> >> >>>showMethods("bar") >>> >>> >>Function "bar": >> A = "vector" >> A = "array" >> A = "integer" >> (inherited from A = "vector") >> A = "matrix" >> (inherited from A = "vector") >> >>But including setMethod("bar", "matrix", ...) in the source code makes bar() >>work fine. >> >> >> >>Paul Roebuck wrote: >>Sorry to bother but could you shed some light on this? >> >> >I don't understand > > >>why order of setMethod calls makes >> >> >any difference. Since it obviously does, > > >>it has shaken >> >> >the foundations of what I thought I understood about >S4 > > >>methods. Even Gabor was surprised... >> >> > > >---------- Forwarded message > > >>---------- >> >> >Date: Wed, 12 Apr 2006 18:24:46 -0400 >From: Gabor Grothendieck > > >><[EMAIL PROTECTED]> >>To: Paul Roebuck <[EMAIL PROTECTED]> Cc: R Help Mailing List >><r-help@stat.math.ethz.ch> Subject: Re: [R] S4 method dispatch matrixOrArray >>On 4/12/06, Paul Roebuck <[EMAIL PROTECTED]> wrote: >> >> >>On Wed, 12 Apr 2006, Gabor Grothendieck wrote: >> >> > > > > >>On 4/12/06, Paul Roebuck <[EMAIL PROTECTED]> wrote: >> >> > > > > >>I have some code where the primary dispatching is on >> >> >other parameters so I'd > > >>like not to have to create a >> >> >set of functions for "matrix" and another > > >>duplicate >> >> >set for "array". But the class union technique isn't >working as > > >>implemented below and I don't have my Green >> >> >book with me. How do I fix my > > >>infinite recursion >>problem? >> >> > > >##-------------------------------------------------------- >library(methods) > >setGeneric("foo", > > >>function(A, ...) { >> >> > cat("generic", match.call()[[1]], "\n") > > >>standardGeneric("foo") >> >> > }) > >setMethod("foo", > signature(A = "vector"), > > >>function(A, ...) { >> >> > callGeneric(matrix(A, nrow = 1), ...) > > >>}) >> >> > >setClassUnion("matrixOrArray", c("matrix", "array")) > >setMethod("foo", > > >>signature(A = "matrixOrArray"), >> >> > function(A, ...) { > cat("A =", A, "\n") > > >>}) >> >> > >## Test >foo(1:4) >foo(matrix(1:4, 1, 4)) >foo(array(1:4, c(1, 4, 1))) > > > >>I think its good enough to just define an array method, i.e. you >> >> >don't need > > >>the matrix method or the matrixOrArray class, and the >> >> >vector method can call > > >>foo(matrix(A,1), ...) so: >> >> > >setGeneric("foo", > function(A, ...) { > > >>cat("generic", match.call()[[1]], "\n") >> >> > standardGeneric("foo") > > >>}) >> >> > >setMethod("foo", > signature(A = "array"), > function(A, ...) { > cat("A > > >>=", A, "\n") >> >> > }) > >setMethod("foo", > signature(A = "vector"), > function(A, > > >>...) { >> >> > foo(matrix(A, nrow = 1), ...) > }) > > > >>Something didn't seem right here. That was pretty close >> >> >to what I had > > >>started with, before trying to go the >> >> >classUnion route. Matter of fact, the > > >>vector method can >> >> >retain use of callGeneric. > >The solution has to do with > > >>the order in which calls to >> >> >setMethod are made. Adding foo-vector after > > >>foo-array >> >> >works fine; the other way around causes infinite recursion. > > > >>This is surprising. I would have thought that the >> >> >parent/child relationships > > >>determine the order that >> >> >dispatched methods are invoked, not the order > > >>that >> >> >the setMethod commands are issued in. > > > > > > >______________________________________________ >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