I have opened a bug report here: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=18138
Regarding _R_S3_METHOD_LOOKUP_USE_TOPENV_AS_DEFENV_… maybe it’s a good time to consider purging it alltogether? This behavior appears to be completely undocumented and this variable is mentioned exactly two times in the R source code. Interestingly enough, utils::.defenv_for_S3_registry does consider it. Best, Taras > On 30 Jun 2021, at 17:38, Duncan Murdoch <murdoch.dun...@gmail.com> wrote: > > One error in my workaround: it also ignores > _R_S3_METHOD_LOOKUP_USE_TOPENV_AS_DEFENV_. If that evaluates to FALSE, it > shouldn't need to make any changes. > > Duncan Murdoch > > On 30/06/2021 9:20 a.m., Taras Zakharko wrote: >> Thanks Duncan, I will submit a bug report to R core then. >> Best, >> Taras >>> On 30 Jun 2021, at 14:16, Duncan Murdoch <murdoch.dun...@gmail.com> wrote: >>> >>> On 30/06/2021 7:37 a.m., Taras Zakharko wrote: >>>> Thats not how I read the code? Consider this snippet from registerS3method: >>>> genfun <- get(genname, envir = envir) >>>> if (.isMethodsDispatchOn() && methods::is(genfun, >>>> "genericFunction")) >>>> genfun <- methods::finalDefaultMethod(genfun@default) >>>> if (typeof(genfun) == "closure") >>>> environment(genfun) >>>> else .BaseNamespaceEnv >>>> This is the environment where the method cache (.__S3MethodsTable__.) will >>>> be updated. The problem hoverer, the with the default setting of >>>> _R_S3_METHOD_LOOKUP_USE_TOPENV_AS_DEFENV_ this is *not* the environment >>>> where R will look for the method cache. Manually injecting the entry into >>>> the method cache of the top environment instead works like a charm. >>>> The envir argument is just for looking up the generic function. >>> >>> Yes, I think you're right: the method is registered in the environment of >>> the generic. I think by default it is searched for in the topenv() of that >>> environment. >>> >>> In most cases those are the same thing, but in your code they aren't the >>> same: the topenv is the global environment. >>> >>> So I think I'd agree this is a bug. I'd say the bug is in >>> registerS3method, which should pay attention to the >>> _R_S3_METHOD_LOOKUP_USE_TOPENV_AS_DEFENV_ setting when it computes defenv. >>> >>> Here's a workaround that copies methods to the appropriate place: >>> >>> fixS3registrations <- function(genericname, envir = parent.frame()) { >>> generic <- get(genericname, envir=envir) >>> genenv <- environment(generic) >>> top <- topenv(genenv) >>> >>> if (!identical(genenv, top)) { >>> toptable <- top[[".__S3MethodsTable__."]] >>> if (is.null(toptable)) { >>> toptable <- new.env(hash = TRUE, parent = baseenv()) >>> top[[".__S3MethodsTable__."]] <- toptable >>> } >>> table <- genenv[[".__S3MethodsTable__."]] >>> if (!is.null(table)) { >>> methodnames <- ls(table, pattern = paste0(genericname, "[.][^.]*$")) >>> for (m in methodnames) >>> toptable[[m]] <- table[[m]] >>> } >>> } >>> } >>> >>> env <- local({ >>> # define the generic function and the method >>> myfun <- function(x) UseMethod("myfun") >>> myfun.myclass <- function(x) print("called myfun.myclass") >>> >>> # register the method >>> .S3method("myfun", "myclass", myfun.myclass) >>> fixS3registrations("myfun") >>> >>> environment() >>> }) >>> >>> env$myfun(structure(0, class = "myclass")) >>> #> [1] "called myfun.myclass" >>> >>> >>> Duncan Murdoch >>> >>>> Best, >>>> Taras >>>>> On 30 Jun 2021, at 13:29, Duncan Murdoch <murdoch.dun...@gmail.com> wrote: >>>>> >>>>> On 30/06/2021 7:23 a.m., Taras Zakharko wrote: >>>>>> I had another glance at the code and now I’m convinced that this is the >>>>>> bug in registerS3method(). Default R behavior (in objects.c) appears to >>>>>> be to look for method definitions in the top environment, not the >>>>>> defining environment, but registerS3method() registers the method in the >>>>>> defining environment. I think registerS3method() should be changed to >>>>>> something like: >>>>> >>>>> It registers wherever you ask it to. The default is the defining >>>>> environment. >>>>> >>>>> .S3method is the one that always uses the defining environment, since it >>>>> has no way to override the default. >>>>> >>>>> Duncan Murdoch >>>>> >>>>>> - if (typeof(genfun) == "closure”) >>>>>> - environment(genfun) >>>>>> ------------ >>>>>> + if (typeof(genfun) == "closure”) >>>>>> + if(isFALSE(Sys.getenv("_R_S3_METHOD_LOOKUP_USE_TOPENV_AS_DEFENV_”)) >>>>>> environment(genfun) else topenv(environment(genfun)) >>>>>> in order to match the behavior of do_usemethod() >>>>>> Best, >>>>>> Taras >>>>>>> On 30 Jun 2021, at 12:51, Taras Zakharko <taras.zakha...@uzh.ch> wrote: >>>>>>> >>>>>>> @Duncan: .S3method() calls registerS3method() with appropriate >>>>>>> environmental argument under the good, so that’s not the problem. >>>>>>> >>>>>>> Anyway, I’ve been doing some debugging and I think I have found the >>>>>>> issue. The following snippet in src/objects.c >>>>>>> (https://github.com/wch/r-source/blob/ecc633b37d77fdd1cb27dda74d7f6b3684f0c01c/src/main/objects.c#L525) >>>>>>> sets the global lookup_use_topenv_as_defenv variable: >>>>>>> >>>>>>> >>>>>>> if(lookup_use_topenv_as_defenv == -1) { >>>>>>> lookup = getenv("_R_S3_METHOD_LOOKUP_USE_TOPENV_AS_DEFENV_"); >>>>>>> lookup_use_topenv_as_defenv = >>>>>>> ((lookup != NULL) && StringFalse(lookup)) ? 0 : 1; >>>>>>> } >>>>>>> >>>>>>> Isn’t that supposed to be >>>>>>> >>>>>>> lookup_use_topenv_as_defenv = ((lookup != NULL) && >>>>>>> StringFalse(lookup)) ? 1 : 0; >>>>>>> >>>>>>> instead? >>>>>>> >>>>>>> The way the code works right now, methods will be looked up in top >>>>>>> environment exactly if _R_S3_METHOD_LOOKUP_USE_TOPENV_AS_DEFENV_ is not >>>>>>> set. This seems incompatible with what registerS3method() does (setting >>>>>>> the .__S3MethodsTable__. on the defining environment instead of the >>>>>>> topenv). When I change 0 and 1 around, everything works as expected. >>>>>>> >>>>>>> In the meantime, I can work around it by manually injecting >>>>>>> __S3MethodsTable__ into .GlobalEnv (which is my topenv here). >>>>>>> >>>>>>> I can open a bug report, but I would like to wait for some more >>>>>>> comments. >>>>>>> >>>>>>> Best, >>>>>>> >>>>>>> Taras >>>>>>> >>>>>>>> On 30 Jun 2021, at 12:39, Joshua Ulrich <josh.m.ulr...@gmail.com> >>>>>>>> wrote: >>>>>>>> >>>>>>>> On Wed, Jun 30, 2021 at 5:17 AM Duncan Murdoch >>>>>>>> <murdoch.dun...@gmail.com> wrote: >>>>>>>>> >>>>>>>>> On 30/06/2021 5:22 a.m., Taras Zakharko wrote: >>>>>>>>>> Dear all, >>>>>>>>>> >>>>>>>>>> I have a generic function and a bunch of methods defined in a >>>>>>>>>> separate environment. Here is a reduced example: >>>>>>>>>> >>>>>>>>>> env <- local({ >>>>>>>>>> # define the generic function and the method >>>>>>>>>> myfun <- function(x) UseMethod("myfun") >>>>>>>>>> myfun.myclass <- function(x) print("called myfun.myclass”) >>>>>>>>>> >>>>>>>>>> # register the method >>>>>>>>>> .S3method("myfun", "myclass", myfun.myclass) >>>>>>>>>> >>>>>>>>>> environment() >>>>>>>>>> }) >>>>>>>>>> >>>>>>>>>> Since the method has been registered, I hoped that invocation like >>>>>>>>>> this would work: >>>>>>>>>> >>>>>>>>>> env$myfun(structure(0, class = "myclass”)) >>>>>>>>>> >>>>>>>>>> However, this results in a “no applicable method" error. >>>>>>>>>> >>>>>>>>>> It is my understanding that registerS3method (called by .S3method) >>>>>>>>>> will install the method string in the .__S3MethodsTable__. table of >>>>>>>>>> the environment where the generic function is defined, and this >>>>>>>>>> table is subsequently used by usemethod() inside R, so I am puzzled >>>>>>>>>> that the dispatch does not work. I checked and the >>>>>>>>>> .__S3MethodsTable__. of env is indeed setup correctly. I also tried >>>>>>>>>> manually adding the method string to the global .__S3MethodsTable__. >>>>>>>>>> inside .BaseNamespaceEnv to no effect. >>>>>>>>>> >>>>>>>>>> In fact, the only way to make it work is to define either myfun or >>>>>>>>>> myfun.myclas in the global environment, which is something I would >>>>>>>>>> like to avoid. >>>>>>>>>> >>>>>>>>>> Thank you in advance for any pointers! >>>>>>>>>> >>>>>>>>> >>>>>>>>> registerS3method has an additional parameter "envir" which I believe >>>>>>>>> would end up set to env in your code. So this works: >>>>>>>>> >>>>>>>>>> eval(expression(myfun(structure(0, class = "myclass"))), envir = env) >>>>>>>>> [1] "called myfun.myclass" >>>>>>>>> >>>>>>>>> You could probably also call registerS3method with envir specified >>>>>>>>> appropriately and get your original expression to work. >>>>>>>>> >>>>>>>> That doesn't seem to work on 4.1.0 for me. The code below worked for >>>>>>>> me in Oct-2020, though I'm not sure what version of R I was using at >>>>>>>> the time. I was slow to upgrade to 4.0, so it was probably the latest >>>>>>>> 3.x version. >>>>>>>> >>>>>>>> env <- new.env() >>>>>>>> local({ >>>>>>>> # define the generic function and the method >>>>>>>> myfun <- function(x) { UseMethod("myfun", x) } >>>>>>>> >>>>>>>> # register the method >>>>>>>> registerS3method("myfun", "myclass", >>>>>>>> function(x) { print("called myfun.myclass") }, >>>>>>>> envir = env) >>>>>>>> }, envir = env) >>>>>>>> attach(env) >>>>>>>> myfun(structure(0, class = "myclass")) >>>>>>>> >>>>>>>> >>>>>>>>> Duncan Murdoch >>>>>>>>> >>>>>>>>> ______________________________________________ >>>>>>>>> R-devel@r-project.org mailing list >>>>>>>>> https://stat.ethz.ch/mailman/listinfo/r-devel >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> -- >>>>>>>> Joshua Ulrich | about.me/joshuaulrich >>>>>>>> FOSS Trading | www.fosstrading.com >>>>>>> >>>>>> ______________________________________________ >>>>>> R-devel@r-project.org mailing list >>>>>> https://stat.ethz.ch/mailman/listinfo/r-devel >>> > ______________________________________________ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel