Fortunately, for R >= 3.6.0, you don't even need the answer by caldwellst. From the blog entry you linked above:
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”.
Additionally, this is also discussed in the docs for the other suggestion of vctrs::s3_register()
:
#' For R 3.5.0 and later, `s3_register()` is also useful when demonstrating
#' class creation in a vignette, since method lookup no longer always involves
#' the lexical scope. For R 3.6.0 and later, you can achieve a similar effect
#' by using "delayed method registration", i.e. placing the following in your
#' `NAMESPACE` file:
#'
#' ```
#' if (getRversion() >= "3.6.0") {
#' S3method(package::generic, class)
#' }
So, you would simply need to not use @importFrom
and instead of @export
, use @exportS3Method package::generic
(See https://github.com/r-lib/roxygen2/issues/796 and https://github.com/r-lib/roxygen2/commit/843432ddc05bc2dabc9b5b22c1ae7de507a00508)
Illustration
So, to illustrate, we can make two very simple packages, foo
and bar
. The package foo
just has a generic foo()
function and default method:
library(devtools)
create_package("foo")
#' foo generic
#'
#' @param x An object
#' @param ... Arguments passed to or from other methods
#' @export
foo <- function(x, ...) {
UseMethod("foo", x)
}
#' foo default method
#'
#' @param x An object
#' @param ... Arguments passed to or from other methods
#' @export
foo.default <- function(x, ...) {
print("Called default method for foo.")
}
After document()
and install()
ing, we create bar
:
create_package("bar")
which creates a bar
method for foo()
:
#' bar method for foo
#'
#' @param x A bar object
#' @param ... Arguments passed to or from other methods
#'
#' @exportS3Method foo::foo
foo.bar <- function(x, ...) {
print("Called bar method for foo.")
}
Importantly, we must load the foo
package before running document()
, or @exportS3Method
won't work. That is,
library(foo)
document()
But, if we do that, we get the following in the NAMESPACE
for bar
:
# Generated by roxygen2: do not edit by hand
S3method(foo::foo,bar)
We have to manually add foo
to "Suggests" in DESCRIPTION
.
Then if we uninstall foo
, we can still install bar
:
> remove.packages("foo")
Removing package from ‘/home/duckmayr/R/x86_64-pc-linux-gnu-library/4.0’
(as ‘lib’ is unspecified)
> install("bar")
✓ checking for file ‘/home/jb/bar/DESCRIPTION’ ...
─ preparing ‘bar’:
✓ checking DESCRIPTION meta-information ...
─ checking for LF line-endings in source and make files and shell scripts
─ checking for empty or unneeded directories
─ building ‘bar_0.0.0.9000.tar.gz’
Running /opt/R/4.0.0/lib/R/bin/R CMD INSTALL \
/tmp/Rtmp5Xgwqf/bar_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/home/jb/R/x86_64-pc-linux-gnu-library/4.0’
* installing *source* package ‘bar’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (bar)
DESCRIPTION
file – ShorthandDESCRIPTION
even if the typical use case is for examples, etc. – Shorthand