---
title: "S3 Method Lookup"
author: "Kurt Hornik"
date: 2019-08-19
categories: ["Internals", "User-visible Behavior"]
tags: ["S3 method lookup"]
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE)
```

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.
