With a generic that uses UseMethod
, NextMethod
works as I expect:
f <- function(...) UseMethod("f")
f.default <- function(...) 5
f.foo <- function(...) {print("hi"); NextMethod()}
x <- structure(1, class = "foo")
f(x) # prints "hi", returns 5
With the following internal generic, a similar approach does not work:
# NB: base::cbind is an internal generic
cbind.default <- function(...) 5
cbind.foo <- function(...) {print("hi"); NextMethod()}
cbind(x) # prints "hi", Error in NextMethod() : generic function not specified
Is NextMethod
usable with internal generics, and if so, what arguments etc. are needed?
This behaviour stems from bespoke machinery for dispatch on ...
in the C-level do_bind
(see bind.c
). It does not use the conventional mechanism for dispatch documented in help("NextMethod")
and implemented in the C-level Dispatch[Any]OrEval()
(see eval.c
). Crucially, method look-up succeeds but the method call is not evaluated in an environment defining the variables .Generic
, .Method
, .Class
, etc. essential for dispatch by NextMethod
.
On one hand, it is well documented that the dispatch mechanism used by [cr]bind
is exceptional (see section "Dispatch" in help("cbind")
). On the other hand, c
is conceptually quite parallel to [cr]bind
, also dispatching internally on ...
, but c
does not have this limitation:
> m <- function(...) { cat("a\n"); NextMethod() }
> for (generic in c("c", "cbind", "rbind")) .S3method(generic, "a", m)
> x <- structure(0, class = "a")
> c(x)
a
[1] 0
> cbind(x)
a
Error in NextMethod() : generic function not specified
> rbind(x)
a
Error in NextMethod() : generic function not specified
> debug(m)
> c(x)
debugging in: c.a(x)
debug at #1: {
cat("a\n")
NextMethod()
}
Browse[1]> ls(all.names = TRUE)
[1] "..." ".Class" ".Generic" ".GenericCallEnv"
[5] ".GenericDefEnv" ".Group" ".Method"
Browse[1]> Q
> cbind(x)
debugging in: cbind(deparse.level, ...)
debug at #1: {
cat("a\n")
NextMethod()
}
Browse[1]> ls(all.names = TRUE)
[1] "..."
Browse[1]> Q
> rbind(x)
debugging in: rbind(deparse.level, ...)
debug at #1: {
cat("a\n")
NextMethod()
}
Browse[1]> ls(all.names = TRUE)
[1] "..."
Browse[1]> Q
It seems quite "wrong" to me that [cr]bind
and c
should behave differently here. So I've filed a bug report: https://bugs.r-project.org/show_bug.cgi?id=18779