Managing imports and exports

The package NAMESPACE is one of the most confusing parts of building a package. roxygen2 aims to make it as easy as possible to build a package that is a well-behaved member of the R ecosystem. This is a little frustrating at first, but soon becomes second-nature.

Exports

In order for your users to use a function1 in your package, you must export it. In most cases, you can just use the @export tag, and roxygen2 will automatically figure out which NAMESPACE directive (i.e. export(), exportS3method(), exportClasses(), orexportMethods()) you need.

Note that datasets should never be exported as they are not found in NAMESPACE. Instead, datasets will either be automatically exported if you set LazyData: true in your DESCRIPTION, or made available after calling data() if not.

Functions

A function should be exported if it is user facing; it should not be exported if it’s for internal use only. If you export a function, you must also document it, and since other people will use it, you need to be careful if you later change the function interface.

#' Add two numbers together
#' 
#' @param x,y A pair of numbers.
#' @export
add <- function(x, y) {
  x + y
}

S3

An S3 generic works like a regular R function so export it following the advice above: if you want users to call it, export; otherwise, don’t.

#' Take an object to bizarro world
#' 
#' @param x A vector.
#' @export
bizarro <- function(x, ...) {
  UseMethod("bizarro")
}

While S3 methods are regular functions with a special naming scheme, their “export” works a bit differently. S3 methods are exported only in the sense that calling the generic with the appropriate class will call the method; a user can’t directly access the method definition by typing its name. A more technically correctly term would be to say that the method is registered so that the generics can find it.

You must register, i.e. @export, every S3 method regardless of whether or not the generic is exported. roxygen2 will warn you if you have forgotten.

#' @export
bizarro.character <- function(x, ...) {
  letters <- strsplit(x, "")
  letters_rev <- lapply(letters, rev)
  vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1))
}

If you are exporting a method in some other way, you can use @exportS3Method NULL to suppress the warning.

You have four options for documenting an S3 method:

#' Take an object to bizarro world
#' 
#' @description
#' This is an S3 generic. This package provides methods for the 
#' following classes:
#' 
#' * `character`: reverses the order of the letters in each element of 
#'    the vector.
#' 
#' @param x A vector.
#' @export
bizarro <- function(x, ...) {
  UseMethod("bizarro")
}

#' @export
#' @rdname bizarro
bizarro.character <- function(x, ...) {
  letters <- strsplit(x, "")
  letters_rev <- lapply(letters, rev)
  vapply(letters_rev, paste, collapse = "", FUN.VALUE = character(1))
}

Typically, you will write methods for generics that are either defined in the current package or a package that is a hard dependency2 of your package. Sometimes, however, you will want to write a method for a suggested dependency. In this case, @export will not work because it assumes the generic is included or imported in your NAMESPACE. Instead, use @exportS3Method. This will use “delayed” method registration, which means the method will only be registered when the suggested package is loaded.

To use @exportS3Method you must provide the package and generic name in the following format:

#' @exportS3Method pkg::generic
generic.foo <- function(x, ...) {
}

S4

Manual exports

If @export does not automatically generate the correct NAMESPACE directive, you can use one of the tags below to exercise greater control:

For even more specialised cases you can use @rawNamespace code which inserts code literally into the NAMESPACE. This is useful if you need a conditional import or export, e.g.

# From dplyr:
#' @rawNamespace import(vctrs, except = data_frame)

# From backports:
#' @rawNamespace if (getRversion() < "4.0.0") export(stopifnot)

If you need to automate this, @evalNamespace fun() will evaluate fun() in the package environment and insert the results into NAMESPACE. Note that because evalNamespace() is run in the package environment, it can only generate exports, not imports.

Imports

The NAMESPACE also controls which functions from other packages are made available to your package.

Functions

If you are using just a few functions from another package, we recommending adding the package to the Imports: field of the DESCRIPTION file and calling the functions explicitly using ::, e.g., pkg::fun().

my_function <- function(x, y) {
  pkg::fun(x) * y
}

If the repetition of the package name becomes annoying you can @importFrom and drop the :::

#' @importFrom pkg fun 
my_function <- function(x, y) {
  fun(x) * y
}

Imports affect every function in a package, so it’s common to collect them in a central place, like {packagename}-package.R. This is automated by usethis::use_import_from().

#' @importFrom pkg fun1 fun2
#' @importFrom pkg2 fun3
#' @importFrom pkg3 fun4
NULL
#> NULL

Note the use of NULL here: you must provide something for roxygen2 to document, so we use NULL as place holder.

It is possible, but not generally recommended to import all functions from a package with @import package. This is risky if you import functions from more than one package, because while it might be ok today, in the future the packages might end up with a function having the same name, and your users will get a warning every time your package is loaded.

S3

S3 generics are just functions, so the same rules for functions apply. S3 methods always accompany the generic, so as long as you can access the generic (either implicitly or explicitly), the methods will also be available. In other words, you don’t need to do anything special for S3 methods. As long as you’ve imported the generic, all the methods will also be available.

S4

Compiled code

To import compiled code from another package, use @useDynLib


  1. Including S3 and S4 generics and methods.↩︎

  2. i.e. it is listed in either the Imports or Depends fields in your DESCRIPTION.↩︎