Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does method inheritance kill additional arguments?

I want to set some flags in my generic (before calling UseMethod(), I know that much :)), and then use and/or update these flags in the methods.

Like so:

g <- function(x) {
  y <- 10
  UseMethod("g")
}
g.default <- function(x) {
  c(x = x, y = y)
}
g.a <- function(x) {
  y <- 5  # update y from generic here
  NextMethod()
}

This works when jumping directly to the default method:

g(structure(.Data = 1, class = "c"))  # here y is never updated
# x  y 
# 1 10 

But when I go through NextMethod(), y mysteriously disappears:

g(structure(.Data = 1, class = "a"))  # here y is updated, but cannot be found
# Error in g.default(structure(.Data = 1, class = "a")) : 
#  object 'y' not found

I have figured out how to fix this, by just passing around y a lot:

f <- function(x, ...) {
  y <- 10
  UseMethod("f")
}
f.default <- function(x, ..., y = 3) {
  c(x = x, y = y)
}
f.a <- function(x, ...) {
  y <- 5
  NextMethod(y = y)
}

which yields

f(structure(.Data = 1, class = "c"))
# x y 
# 1 3 
f(structure(.Data = 1, class = "a"))
# x y 
# 1 5 

My question is: Why does NextMethod() in the above g().*-example kill the additional y argument?

I thought the whole point of UseMethod() and NextMethod() was to pass on any and all objects from call to call, without having to manually pass them on:

NextMethod works by creating a special call frame for the next method. If no new arguments are supplied, the arguments will be the same in number, order and name as those to the current method but their values will be promises to evaluate their name in the current method and environment.

  • help("UseMethod")

I'm particularly confused that UseMethod() does seem to pass on y, but NextMethod() doesn't.

like image 888
maxheld Avatar asked Feb 14 '18 08:02

maxheld


1 Answers

As @Roland pointed out, this behavior is documented:

From help("UseMethod") the paragraph about UseMethod() notes (emphasis added):

UseMethod creates a new function call with arguments matched as they came in to the generic. Any local variables defined before the call to UseMethod are retained (unlike S). Any statements after the call to UseMethod will not be evaluated as UseMethod does not return.

The respective paragraph about NextMethod() (already cited above) merely notes:

NextMethod works by creating a special call frame for the next method. If no new arguments are supplied, the arguments will be the same in number, order and name as those to the current method but their values will be promises to evaluate their name in the current method and environment. Any named arguments matched to … are handled specially: they either replace existing arguments of the same name or are appended to the argument list. They are passed on as the promise that was supplied as an argument to the current environment. (S does this differently!) If they have been evaluated in the current (or a previous environment) they remain evaluated. (This is a complex area, and subject to change: see the draft ‘R Language Definition’.)

In short then UseMethod() does something special and extraordinary: it passes on local variables. NextMethod(), as per usual, doesn't do this.

UseMethod() is the exception, not NextMethod().

like image 159
maxheld Avatar answered Nov 13 '22 00:11

maxheld