R's UseMethod() does not dispatch on changed class() (PR#167)

rgentlem@hsph.harvard.edu rgentlem@hsph.harvard.edu
Thu, 15 Apr 1999 15:37:16 +0200


I've done some editing to get things together (hopefully without
changing the semantics)

On Thu, 15 Apr 1999, Prof Brian D Ripley wrote:

> On Wed, 14 Apr 1999, Robert Gentleman wrote:
> 
> > On Wed, 14 Apr 1999, Prof Brian D Ripley wrote:
> > 
> > > On 15 Apr 1999, Peter Dalgaard BSA wrote:
> > > 
> > > > Robert Gentleman <rgentlem@hsph.harvard.edu> writes:
> > > > 
> 
> I did not mean to imply that you were wrong, rather that the White Book
> can be read both ways (and it seems has been).
> 
> > But, it as I understand it, UseMethod is supposed to call the method
> > with the arguments the same as those that called the generic. You
> > must evaluate x to figure out what method to call, but I don't see
> > how you can change x (adding classes is changing it).
> 
> Then why are
> 
> UseMethod("foo")
> UseMethod("foo", x)
> different
> 
> 
> and by the standard evaluation model, `object' is subject to lazy
> evaluation. Nothing I can see in the White Book says otherwise. Indeed,
> page 467 says the arguments are `re-matched by the standard rules'. It then
> says `The method will see argument matches as it would if the user's call
> had been directly to the method'.  I think those claims are contradictory,
> and in particular whichever way they are read suggest my pair of calls
> should be the same.
> 
> [In S UseMethod("foo") is semantic shorthand for UseMethod("foo", <name of
> first argument of caller>), according to the help page.]
> 

  Now, with my reading, the method will see the argument as it would
if the users call had been directly to the the method, coupled with
the later comment (top of p. 469) that for NextMethod you must take local
bindings for the arguments, yields
   with UseMethod you don't take local bindings, the method gets what
the generic did, but it will rematch (argument names and positions
may have changed). That means you need to ignore what happened
inside of the generic.

  Now, we have the variable definition problem raise it's head,
  UseMethod("foo")
  UseMethod("foo",x)
  If the second version doesn't use the local binding of x then
 UseMethod behaves differently, in terms of how it treats its arguments
 than every other function (which is ok, we just need to be specific
 about it).
  So we have to make a decision, UseMethod does what S does (which is
 fine) or it doesn't.

> To make matters worse, in S it is a common mistake to have
> 
> foo <- function(x, ...) UseMethod("foo")
> foo.lm <- function(y, ...) { .... }
> 
> and that change of name of argument causes re-evaluation.  What happens in
> R?  
  The standard argument matching system takes over. If there was an
argument y=blah, in the ... then it will get matched to y in foo.lm
even though something different was matched to x in the call to the 
generic. I don't believe that a reevaluation takes place in either 
situation. If x was evaluated and that is what is matched then we
look in the value side of the expression, see that it's there and
carry on. If we match to something new then the standard rules take
over.
  Promises are basically cells with three slots, an unevaluated
expression, an environment in which to carry out the evaluation
of that expression and a value. When we want a value, we look in
the value slot first, if there is no value, we evaluate the
expression and stick the value into the value slot. So promises
should never be evaluated twice.

 For those not bored, here is a difference between R and S that 
we may not want.
bar<-function(w,...) {
    UseMethod("bar")
  }
 bar.foo<-function(y, w) print(w)
x<-30
class(x)<-"foo"

Now in R,
> bar(x,10)
[1] 10
> bar(x,y=10)
[1] 30
attr(,"class")
[1] "foo"

Notice that if the arguments are not named we use default matching and
that the symbolic name for the first argument in the generic is not
carried over.

In S bar(x,10) and bar(x,y=10) both evaluate to the same thing,
[1] 30
attr(,"class")
[1] "foo"
it appears that the name w has been applied to the first argument and
that is carried with it for subsequent evaluations.

  I'm not sure what you meant by "the change of name causes reevaluation,
when I try
> bar(x<-x+1)
I get,
[1] 31
attr(, "class"):
[1] "foo"
[1] 31
attr(, "class"):
[1] "foo"
  which is only one evaluation (and the same in R)

> This makes an apparently small point, that generics should have the
> same argument name as S, potentially rather important. I know of four
> outstanding discrepancies:
> 
> R: deviance <- function (x, ...) UseMethod("deviance")
> S: deviance <- function (x, ...) UseMethod("deviance")
> 
> and ditto for coef, fitted, residuals (which I had not realized, in part
> because some of these only recently acquited defaults).
> 
> There are inconsistencies in the current R code: any objections if
> I change these to uniformly follow S and use `object'?

  Change to your hearts content. I think we want something that works
and that makes sense.
> What we do need is to get this documented!  In particular, in R
> 
> > ?UseMethod
> 
> Class Methods
> 
>      UseMethod(name)
> 


-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
r-devel mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
Send "info", "help", or "[un]subscribe"
(in the "body", not the subject !)  To: r-devel-request@stat.math.ethz.ch
_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._