It's a bug resulting from the combination of:
1. multiple recursive levels of callNextMethod()
2. nonstandard arguments in the method definition; that is, (.Object,
x) vs .Object, ...) for the generic.
Specifically, the callNextMethod code tries to build up a list of
"excluded" classes, but in this example seems to fail. So the
nextMethod selected for class "B", when it goes to find the next^2
method, forgets to exclude class "C"; hence it goes back to the "C"
method & so infinite recursion.
To see what's happening, use trace(callNextMethod, recover) or equivalent.
The computation in addNextMethod, callNextMethod, etc. is all very
heuristic (aka "kludgey") code, so it's not clear how easy a fix would
be. The desirable route would be a better treatment of nonstandard
arguments, but that may require digging deeper into the R evaluator than
I've had the nerve to do so far.
Meanwhile, a work around is to avoid mechanism 2 if you want to use
mechanism 1. This may require separating out the guts of each method
from the callNextMethod() part, with that part coming in a method
definiton with standard arguments.
With the alternative definition below, all is well (if not very useful):
> new("C", a=1,b=2,c=3)
in C
in B
in A
An object of class "C"
Slot "c":
[1] 3
Slot "b":
[1] 2
Slot "a":
[1] 1
using the methods:
setMethod("initialize", signature(.Object="A"),
function(.Object, ...) {
cat("in A\n")
callNextMethod()
})
setMethod("initialize", signature(.Object="B"),
function(.Object, ...) {
cat("in B\n")
callNextMethod()
})
setMethod("initialize", signature(.Object="C"),
function(.Object, ...) {
cat("in C\n")
callNextMethod()
})
Seth Falcon wrote:
>Hi,
>
>Given a simple three class hierarchy: A <-- B <-- C
>
>I want to define an initialize method for each class such that when I
>call new("C", x=5), the initialize methods for A and B are used to
>incrementally build the object.
>
>When I do what seems obvious to me using callNextMethod, I get an
>infinite recursion. An example follows...
>
>setClass("A", representation(a="numeric"))
>setClass("B", representation(b="numeric"), contains="A")
>setClass("C", representation(c="numeric"), contains="B")
>
>setMethod("initialize", signature(.Object="A"),
> function(.Object, x) {
> cat("in A\n")
> [EMAIL PROTECTED] <- x
> .Object
> })
>
>setMethod("initialize", signature(.Object="B"),
> function(.Object, x) {
> cat("in B\n")
> .Object <- callNextMethod(.Object=.Object, x=x)
> [EMAIL PROTECTED] <- [EMAIL PROTECTED] + 1
> .Object
> })
>
>setMethod("initialize", signature(.Object="C"),
> function(.Object, x) {
> cat("in C\n")
> .Object <- callNextMethod(.Object=.Object, x=x)
> [EMAIL PROTECTED] <- [EMAIL PROTECTED] + [EMAIL PROTECTED] + 1
> .Object
> })
>
>
>## Works as I am expecting for B
>
>
>>myB <- new("B", 4)
>>
>>
>in B
>in A
>
>## When you create a C, the B method gets called but the appropriate
>## next method info seems lost and we end up back in C's method ?!
>
>
>>myC <- new("C", 5)
>>
>>
>in C
>in B
>in C
>in B
>in C
> C-c C-c
>
>Should this work? Is there a better way to organize the initializers
>for a simple hierarchy (perhaps assume that args to the initializers
>are not the same for A, B, and C).
>
>Thanks,
>
>+ seth
>
>______________________________________________
>[email protected] mailing list
>https://stat.ethz.ch/mailman/listinfo/r-devel
>
>
>
[[alternative HTML version deleted]]
______________________________________________
[email protected] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel