I would like to create and attach
an environment
containing S3 methods and have R find them in the search path, by using the method name (i.e., I should be able to get infix-style a + b
to work and not have to write prefix-style "+.Foo"(a, b)
). What am I doing wrong? Although rather longish, the shortest example that I could come up within a reasonable amount of time is below. The comments elaborate on the the situation and problem.
Update: I have appended some more code per the selected answer.
# **** CLEAR THE USER WORKSPACE / GLOBAL ENVIRONMENT ****
rm(list = ls( all = TRUE ))
# **** RESET THE SEARCH PATH. (I'VE NEVER HAD AN R INSTALLATION THAT DOESN'T BOOT ****
# **** WITH package:stats AS THE SECOND-TO-LAST ITEM ON THE SEARCH PATH.) ****
while (search()[[2]] != "package:stats") detach()
search ()
# [1] ".GlobalEnv" "package:stats" "package:graphics" "package:grDevices" "package:utils"
# [6] "package:datasets" "package:methods" "Autoloads" "package:base"
x <- 1L
class (x) <- "Foo"
`+.Foo` <- function ( x , y ) {
cat("This is `x.Foo`\n")
# "non-standard" addition on purpose
z <- -1L * ( as.integer(x) + as.integer(y) )
class(z) <- "Foo"
z }
x + x
# This is `x.Foo`
# [1] -2
# attr(,"class")
# [1] "Foo"
# Note that R finds `x.Foo` in the global environment.
FooEnv <- new.env(parent = as.environment(search()[[2]]))
attr(FooEnv, "name") <- "FooEnv"
FooEnv
# <environment: 0xhhhhhhhhhhhhhhhh> ## Each h is a hex-digit.
# attr(,"name")
# [1] "FooEnv"
parent.env(FooEnv)
# <environment: package:stats>
# attr(,"name")
# [1] "package:stats"
# attr(,"path")
# [1] "C:/Program Files/R/R-4.0.3/library/stats"
# The path above will vary with the R installation.
# I want the next 3 lines of R code is to replace `+.Foo` in the global environment
# with `x.Foo` on the search path.
FooEnv $ `+.Foo` <- `+.Foo`
rm(`+.Foo`)
attach(what = FooEnv, name = attr(FooEnv, "name"))
search()
# [1] ".GlobalEnv" "FooEnv" "package:stats" "package:graphics" "package:grDevices"
# [6] "package:utils" "package:datasets" "package:methods" "Autoloads" "package:base"
x + x
# [1] 2
# attr(,"class")
# [1] "Foo"
## Note lack of "This is `x.Foo`".
## Note the sign.
## This is not the `+` we're looking for.
# `methods` finds `+.Foo`:
methods("+")
# [1] +.Date +.Foo +.POSIXt
# see '?methods' for accessing help and source code
# `find` finds `+.Foo`
find("+.Foo")
# [1] "FooEnv"
# Although the following works, I want to call `x.Foo` using infix style.
`+.Foo`(x, x)
# This is `x.Foo`
# [1] -2
# attr(,"class")
# [1] "Foo"
write.dcf(R.Version())
# platform: x86_64-w64-mingw32
# arch: x86_64
# os: mingw32
# system: x86_64, mingw32
# status:
# major: 4
# minor: 0.3
# year: 2020
# month: 10
# day: 10
# svn rev: 79318
# language: R
# version.string: R version 4.0.3 (2020-10-10)
# nickname: Bunny-Wunnies Freak Out
# `.S3method` seems to do the trick. Acknowledgement: @MrFlick
.S3method("+", "Foo")
x + x
# This is `x.Foo`
# [1] -2
# attr(,"class")
# [1] "Foo"
If you aren't registering an S3 method as part of a package namespace or in the global environment, you'll need to explicitly register it using the .S3method()
function. So in this case you would do
.S3method("+", "Foo", FooEnv$`+.Foo`)
This issue is further discussed here: https://developer.r-project.org/Blog/public/2019/08/19/s3-method-lookup/