I'm trying to figure out how NextMethod()
works. The most detailed explanation I have found of the S3 class system is in Chambers & Hastie (edts.)'s Statistical Models in S (1993, Chapman & Hall), however I find the part concerning NextMethod
invocation a little obscure. Following are the relevant paragraphs I'm trying to make sense of (pp. 268-269).
Turning now to methods invoked as a result of a call to
NextMethod()
, these behave as if they had been called from the previous method with a special call. The arguments in the call to the inherited method are the same in number, order, and actual argument names as those in the call to the current method (and, therefore, in the call to the generic). The expressions for the arguments, however, are the names of the corresponding formal arguments of the current method. Suppose, for example, that the expressionprint(ratings)
has invoked the methodprint.ordered()
. When this method invokesNextMethod()
, this is equivalent to a call toprint.factor()
of the formprint.factor(x)
, wherex
is here thex
in the frame ofprint.ordered()
. If several arguments match the formal argument "...
", those arguments are represented in the call to the inherited method y special names "..1
", "..2
", etc. The evaluator recognizes these names and treats them appropriately (see page 476 for an example).This rather subtle definition exists to ensure that the semantics of function calls in S carry over as cleanly as possible to the use of methods (compare Becker, Chambers and Wilks's The New S Language, page 354). In particular:
- Arguments are passed down from the current method to the inherited method with their current values at the time
NextMethod()
is called.- Lazy evaluation continues in effect; unevaluated arguments stay unevaluated.
- Missing arguments remain missing in the inherited method.
- Arguments passed through the "
...
" formal argument arrive with the correct argument name.- Objects in the frame that do not correspond to actual arguments in the call will not be passed to the inherited method."
The inheritance process is essentially transparent so far as the arguments go.
Two points that I find confusing are:
Generally speaking, if anyone could please restate the description given in the above paragraphs in a lucider fashion, I'd appreciate it.
Consider this example where generic function f
is called and it invokes f.ordered
and then, using NextMethod
, f.ordered
invokes f.factor
:
f <- function(x) UseMethod("f") # generic
f.ordered <- function(x) { x <- x[-1]; NextMethod() }
f.factor <- function(x) x # inherited method
x <- ordered(c("a", "b", "c"))
class(x)
## [1] "ordered" "factor"
f(x)
## [1] b c
## Levels: a < b < c
Now consider the original text:
Turning now to methods invoked as a result of a call to NextMethod(), these behave as if they had been called from the previous method with a special call.
Here f
calls f.ordered
which calls f.factor
so the method "invoked as a
result of a call to NextMethod" is f.factor
and the previous method is
f.ordered
.
The arguments in the call to the inherited method are the same in number, order, and actual argument names as those in the call to the current method (and, therefore, in the call to the generic). The expressions for the arguments, however, are the names of the corresponding formal arguments of the current method. Suppose, for example, that the expression print(ratings) has invoked the method print.ordered(). When this method invokes NextMethod(), this is equivalent to a call to print.factor() of the form print.factor(x), where x is here the x in the frame of print.ordered()
Now we switch perspectives and we are sitting in f.ordered
so now f.ordered
is the current method and f.factor
is the inherited method.
At the point that f.ordered
invokes NextMethod()
a special call is constructed
to call f.factor
whose arguments are the same as those passed to f.ordered
and
to the generic f
except that they refer to the versions of the arguments in f.ordered
(which
makes a difference here as f.ordered
changes the argument before invoking
f.factor
.
Hard to go through all this post, but I think that this small example can help to demystify the NextMethod
dispatching.
I create an object with 2 classes attributes (inheritance) 'first' and 'second'.
x <- 1
attr(x,'class') <- c('first','second')
Then I create a generic
method Cat
to print my object
Cate <- function(x,...)UseMethod('Cate')
I define Cate
method for each class.
Cate.first <- function(x,...){
print(match.call())
print(paste('first:',x))
print('---------------------')
NextMethod() ## This will call Cate.second
}
Cate.second <- function(x,y){
print(match.call())
print(paste('second:',x,y))
}
Now you can can check Cate
call using this example:
Cate(x,1:3)
Cate.first(x = x, 1:3)
[1] "first: 1"
[1] "---------------------"
Cate.second(x = x, y = 1:3)
[1] "second: 1 1" "second: 1 2" "second: 1 3"
Cate.second(x = x, y = 1:3)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With