Fooling R’s Method Dispatch

Fooling R’s Method Dispatch


6

I am looking for an example where a dot in a function name causes R’s Method Dispatch to pick the wrong function. A natural candidate seems to be the builtin function t.test(), which should be confused with t() when applied to an object of class "test".

Strangely, however, the following code actually calls the transposition function and not t.test():

> x <- structure(cbind(1:4,2:5), class="test")
> class(x)
[1] "test"
> t(x)
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    2    3    4    5

This raises two questions:

  1. Why is the object transposed and not t.test() invoked, as it should be due to the rules of Method Dispatch?
  2. What is a (simple) example for messing up the Method Dispatch process by defining a function with a dot inside?

8

  • 2

    Interestingly getS3method(), inconsistently with actual S3 dispatch, would actually return t.test here. I believe that this is a bug (at the very least it should give consistent results!) but when asking about this on the r-devel mailing list I got basically no response, but I got the impression that even (some) core R maintainers do not fully understand S3 dispatch.

    – Konrad Rudolph

    8 hours ago

  • Also, try registerS3method('t', 'test', t.test) before calling t(x): it will cause t.test() to be invoked. This behaviour changed with R 3.5.

    – Konrad Rudolph

    8 hours ago


  • My "vote," such as it is, would be to force the stats library to rename t.test to something either without the "dot" (I know, flamewar), or better yet, change the name to something unique and obvious like studentT . Since t.test is in fact Not a method belonging to t, applying t() to an overloaded class name should never invoke t.test because it's not a sub-method of anything. Is this intended or is it a bug in the interpreter/dispatcher?

    – Carl Witthoft

    7 hours ago


  • @carl-witthoft There are different methods to define classes in R, and for S4 classes and Reference Classes, you are right. For S3 classes, however, t.test() is a method of a class "test", provided such a class exists and there is a generic function t(). This is just an ambiguity inherent to the S3 way of dealing with classes. Actually, my question was, how to abuse this ambiguity for teaching purposes: I want to explain why, e.g., the Google coding style guide discourages the use of dots in function names.

    – cdalitz

    6 hours ago


  • You say "the rules of Method Dispatch", but didn't say where you read them. There are mistakes in the manuals that come with R; those are worth reporting to the developers. There are also mistakes in documentation written by other people. Sometimes those are worth reporting to those authors, but often it's just not worth the trouble.

    – user2554330

    5 hours ago

2 Answers
2


3

You also have to consider the environment where functions are evaulated. For example, if you ran this in the stats environment, you would get

t(x)
#      [,1] [,2] [,3] [,4]
# [1,]    1    2    3    4
# [2,]    2    3    4    5
# attr(,"class")
# [1] "test"

evalq(t(x), envir=asNamespace("stats"))
#   One Sample t-test
# 
# data:  x
# t = 6.4807, df = 7, p-value = 0.0003402
# alternative hypothesis: true mean is not equal to 0
# 95 percent confidence interval:
#  1.905392 4.094608
# sample estimates:
# mean of x 
#         3 

So the issue is that the t() function is defined in the base namespace while t.test() is in stats. The transpose is found first by default.


1

S3 dispatch is complicated. If you define a function named t.test in the global environment and call t() there on an object with class "test", the t.test() function will be called. However, if t.test is in a package, it needs to be registered as an S3 method to be handled that way. (Registration happens in the NAMESPACE file.)

So here’s an example of what you are looking for:

t.test <- t.test
x <- 1
class(x) <- "test"
t(x)
#> Error in t.test.default(x): not enough 'x' observations

Created on 2023-11-14 with reprex v2.0.2

There was some news about this when R 3.5.0 was released in 2018: the 8th "new feature" from the end in

https://developer.r-project.org/blosxom.cgi/R-3-5-branch/NEWS/2018/03/29#n2018-03-29



Leave a Reply

Your email address will not be published. Required fields are marked *