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:
- Why is the object transposed and not
t.test()
invoked, as it should be due to the rules of Method Dispatch? - What is a (simple) example for messing up the Method Dispatch process by defining a function with a dot inside?
8
2 Answers
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.
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
Interestingly
getS3method()
, inconsistently with actual S3 dispatch, would actually returnt.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.8 hours ago
Also, try
registerS3method('t', 'test', t.test)
before callingt(x)
: it will causet.test()
to be invoked. This behaviour changed with R 3.5.8 hours ago
My "vote," such as it is, would be to force the
stats
library to renamet.test
to something either without the "dot" (I know, flamewar), or better yet, change the name to something unique and obvious likestudentT
. Sincet.test
is in fact Not a method belonging tot
, applyingt()
to an overloaded class name should never invoket.test
because it's not a sub-method of anything. Is this intended or is it a bug in the interpreter/dispatcher?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 functiont()
. 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.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.
5 hours ago