Iñaki Ucar
2019-May-14 09:33 UTC
[Rd] [R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.
CCing r-devel. On Tue, 14 May 2019 at 02:11, Pavel Krivitsky <pavel at uow.edu.au> wrote:> > Dear All, > > I've run into this while updating a package with unfortunately named > legacy functions. It seems like something that might be worth changing > in R, and I want to get a sense of whether this is a problem before > submitting a report to the Bugzilla. > > It appears that the 3-argument form of S3method() in NAMESPACE controls > dispatching when the generic is called from outside the package that > defines it but not when it's called from inside the package that > defines it. > > For example the attached minimal package has four R functions: > > gen <- function(object, ...) > UseMethod("gen") > > .gen.formula <- function(object, ...){ > message("I am the S3method-declared method.") > } > > gen.formula <- function(object, ...){ > message("I am the function with an unfortunate name.") > } > > test_me <- function(){ > message("I am the tester. Which one will I call?") > gen(a~b) > } > > and the following NAMESPACE: > > export(gen) > S3method(gen, formula, .gen.formula) > export(gen.formula) > export(test_me) > > Now, > > library(anRpackage) > example(test_me) > > results in the following: > > test_m> test_me > function(){ > message("I am the tester. Which one will I call?") > gen(a~b) > } > <bytecode: 0x562fb9d32d68> > <environment: namespace:anRpackage> > > test_m> test_me() # Calls gen.formula() > I am the tester. Which one will I call? > I am the function with an unfortunate name. > > test_m> gen(a~b) # Calls .gen.formula() > I am the S3method-declared method. > > So, calling the same generic function with the same class results in > different dispatching behaviour depending on whether the call is from > within the package doing the export or from the outside.It does not depend on whether you export gen.formula() or not. When you call gen() inside your package, the S3 dispatch mechanism finds a method gen.formula defined in that environment (the package's namespace), so it is called.> This behaviour appears to be as documented ( > https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Method-dispatching > ), but it seems to me that if S3method() is allowed to give the name of > the method to be used, then it should should override the name-based > dispatching both inside and outside the package. > > Any thoughts?Note that disabling name-based dispatch implies two things: 1) the inability to override your method by defining gen.formula in the global environment, and 2) another package can break yours (i.e., internal calls to gen()) by registering an S3 method for gen() after you. I don't think that's a good idea. I?aki
Pavel Krivitsky
2019-May-14 10:31 UTC
[Rd] [R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.
Hi, I?aki, Thanks for looking into this. On Tue, 2019-05-14 at 11:33 +0200, I?aki Ucar wrote:> > So, calling the same generic function with the same class results > > in different dispatching behaviour depending on whether the call is > > from within the package doing the export or from the outside. > > It does not depend on whether you export gen.formula() or not. When > you call gen() inside your package, the S3 dispatch mechanism finds a > method gen.formula defined in that environment (the package's > namespace), so it is called.Indeed, this is the case. The issue is that when both are available and exported, S3method takes precedence outside the package but function name takes precedence inside the package.> Note that disabling name-based dispatch implies two things: 1) the > inability to override your method by defining gen.formula in the > global environment, and 2) another package can break yours (i.e., > internal calls to gen()) by registering an S3 method for gen() after > you.That's a good point. > library(anRpackage) > gen(a~b) I am the S3method-declared method. > gen.formula <- function(object, ...){message("I am the externally declared method.")} > gen(a~b) I am the externally declared method. > test_me() I am the tester. Which one will I call? I am the function with an unfortunate name. In that case, I think that the least surprising behaviour would prioritise declarations and methods "nearer" to the caller over those "farther" from the caller (where "caller" is the caller of the generic, not the generic itself), and, within that, give precedence to S3method declarations over function names. That is, for a call from inside a package, the order of precedence would be as follows: 1. S3method() in that package's NAMESPACE. 2. Appropriately named function?in that package (exported or not). 3. Appropriately named function in calling environment (which may be GlobalEnv). 4. S3method() in other loaded packages' NAMESPACEs. 5. Appropriately named functions exported by other loaded packages' NAMESPACEs. For a call from outside a package, the precedence is the same, but 1 and 2 are not relevant. As far as I can tell, this is the current behaviour except for the relative ordering of 1 and 2. Best, Pavel -- Pavel Krivitsky Lecturer in Statistics National Institute of Applied Statistics Research Australia (NIASRA) School of Mathematics and Applied Statistics | Building 39C Room 154 University of Wollongong NSW 2522 Australia T +61 2 4221 3713 Web (NIASRA): http://niasra.uow.edu.au/index.html Web (Personal): http://www.krivitsky.net/research ORCID: 0000-0002-9101-3362 NOTICE: This email is intended for the addressee named and may contain confidential information. If you are not the intended recipient, please delete it and notify the sender. Please consider the environment before printing this email. -------------- next part -------------- A non-text attachment was scrubbed... Name: anRpackage_1.0.tar.gz Type: application/x-compressed-tar Size: 750 bytes Desc: anRpackage_1.0.tar.gz URL: <https://stat.ethz.ch/pipermail/r-devel/attachments/20190514/c7e72e56/attachment.bin>
Iñaki Ucar
2019-May-14 10:50 UTC
[Rd] [R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.
On Tue, 14 May 2019 at 12:31, Pavel Krivitsky <pavel at uow.edu.au> wrote:> > > Note that disabling name-based dispatch implies two things: 1) the > > inability to override your method by defining gen.formula in the > > global environment, and 2) another package can break yours (i.e., > > internal calls to gen()) by registering an S3 method for gen() after > > you. > > That's a good point. > > > library(anRpackage) > > gen(a~b) > I am the S3method-declared method. > > gen.formula <- function(object, ...){message("I am the externally declared method.")} > > gen(a~b) > I am the externally declared method. > > test_me() > I am the tester. Which one will I call? > I am the function with an unfortunate name. > > In that case, I think that the least surprising behaviour would > prioritise declarations and methods "nearer" to the caller over those > "farther" from the caller (where "caller" is the caller of the generic, > not the generic itself), and, within that, give precedence to S3method > declarations over function names.The thing is that, in R, "nearer" means "the calling environment" (and then, other things). When you call test_me(), the calling environment for gen() is the package namespace. When you call gen() directly, then the calling environment is the global environment. So what happens here follows the principle of least astonishment. The issue here is that you are registering a non-standard name (.gen.formula) for that generic and then defining what would be the standard name (gen.formula) for... what purpose? IMHO, this is a bad practice and should be avoided.> That is, for a call from inside a package, the order of precedence > would be as follows: > 1. S3method() in that package's NAMESPACE. > 2. Appropriately named function in that package (exported or not). > 3. Appropriately named function in calling environment (which may be > GlobalEnv). > 4. S3method() in other loaded packages' NAMESPACEs. > 5. Appropriately named functions exported by other loaded packages' > NAMESPACEs. > > For a call from outside a package, the precedence is the same, but 1 > and 2 are not relevant. > > As far as I can tell, this is the current behaviour except for the > relative ordering of 1 and 2.Nope. Current behaviour (see details in ?UseMethod) is: "To support this, UseMethod and NextMethod search for methods in two places: in the environment in which the generic function is called, and in the registration data base for the environment in which the generic is defined". Changing this would probably break a lot of things out there. I?aki
Possibly Parallel Threads
- [R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.
- [R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.
- [R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.
- [R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.
- [R-pkg-devel] Three-argument S3method declaration does not seem to affect dispatching from inside the package.