Kurt Hornik: S3 Method Lookup

At the core of the S3 object system as introduced in the White Book lies the idea that (S3) methods are ordinary functions that follow the GEN.CLS naming convention (with GEN.default as a final fallback). In the initial R implementation of this object system, these methods were searched for in the environment (and all enclosing environments) from which the generic was called.

With the advent of namespaces (see Tierney (2003), “Name space management for R”, R News, 3(1):2-6) a mechanism for registering S3 methods via S3method() directives in the NAMESPACE file of a package was added. Using S3method(GEN, CLS) or S3method(GEN, CLS, FUN) registers function named, respectively, GEN.CLS or FUN for dispatch without the need for exporting it. Using the latter (3-argument) directive allows to bypass the GEN.CLS naming convention. The generic used in the registration is that “as seen from the package”, and hence needs to be available when the namespace is loaded. This registration mechanism is available for all packages (as they all have a namespace), but not in script code (outside of packages).

Initially, the S3 registry would be consulted only when no appropriate method was found in the calling environment. Methods in base traditionally all follow follow the GEN.CLS naming convention and hence were not registered, as they would be found in .BaseNamespaceEnv as the last element of the static part of the package namespace environments, and hence ahead of the search path starting with the global environment (.GlobalEnv, the user’s workspace) and ending with the base environment (.BaseEnv). However, all registered methods could be shadowed by GEN.CLS exports in attached packages (found on the search path ahead of the registry). To make S3 lookup both more safe and more efficient, it was changed in R 3.5.0 to use the registry after the top level environment (see ?topenv) of the calling environment. Alongside, all S3 methods in base are now registered as well.

Since R 3.6.0, S3method() directives in NAMESPACE can also be used to perform delayed S3 method registration. With S3method(PKG::GEN, CLS, FUN) function FUN will get registered as an S3 method for class CLS and generic GEN from package PKG only when the namespace of PKG is loaded. This can be employed to deal with situations where the method is not “immediately” needed, and having to pre-load the namespace of pkg (and all its strong dependencies) in order to perform immediate registration is considered too “costly”.

Since c76951 in the trunk (committed on 2019-08-10), there is a .S3method() function for registering S3 methods in scripts. Again, this allows to register methods named differently than GEN.CLS.

With all these registration enhancements in place, finding S3 methods via the search path, between .GlobalEnv and .BaseEnv, is no longer necessary, and hence with c77043 in the trunk (committed on 2019-08-19) is turned off by default. This is currently controllable via an internal environment variable, which will disappear eventually, as relying on such in-between search path lookups is both unsafe and inefficient. This may seem to be a major change: but in fact, the CRAN regular checks have been performed with in-between search path lookups turned off for more than a year now, and all check issues stemming from this change have long been eliminated. Similarly, Bioconductor will have all missing S3 method registrations added to its packages for the upcoming 3.10 release.