4

First of all, I have read around this topic a lot. I have studied the conversations here, here, here and here. The problem is, however, I still think one particular topic isn't discussed fully. I am developing a package and I wish to create my own method using the autoplot() generic from the ggplot2 package, i.e. have a function such as autoplot.MyFunction().

My current DESCRIPTION file contains Depends: ggplot2 and everything works, no problem. I also combine this with Roxygen2 tags @import ggplot2 within my function's help file code, coupled with @export.

However most documentation describes how one should try to use Imports: ggplot2. The trouble is, if I make this change, when I load my package with library(my_package) and try to use autoplot.MyFunction(), I am faced with the following error:

> autoplot(tmp)
Error: could not find function "autoplot"

Similarly, if I call the function directly...

> autoplot.MyFunction(tmp)
Error: could not find function "autoplot.MyFunction"

However if I use the :: method, then it does work...

> ggplot2::autoplot(tmp)

From my understanding, this is because Imports loads the ggplot2 package (and therefore its functions), but doesn't attach it, whereas Depends does attach it.

So, finally, my question is simple, am I correct in thinking that to use package generics, I should be using Depends: package, i.e. in my case Depends: ggplot2.

Then for using functions from packages within my package functions I should be using Imports: package coupled with ::, e.g.:

silly_fn <- function (data) {
  p <- ggplot(tmp, aes(x, y)) + 
    geom_line() + 
    geom_segment(aes(x = 0, xend = 20, y = 0, yend = 20), 
                 arrow = grid::arrow(length = grid::unit(0.15, "inches")))
  p
}

would require Imports: grid and a Roxygen2 tag @import grid.

I think this is all correct?

Community
  • 1
  • 1
nathaneastwood
  • 3,664
  • 24
  • 41

1 Answers1

3

I'm not a complete expert on this, but here is something that works:

#' @importFrom ggplot2 autoplot
#' @export autoplot

autoplot <- autoplot

#' @export "autoplot.foo"

autoplot.foo <- function(obj, ...) {
  cat("bar\n")
}

Unfortunately we need to explicitly export the method because for some reason S3 dispatch from a package namespace (in this case ggplot2) doesn't find the method registered from a package it doesn't import from. This kind of makes sense except that it works for base generics so there is a subtlety of the S3 registration mechanism that I'm not privy to.

Re whether to use :: within your package or not, it's up for debate. You don't have to if you use @import or @importFrom, but some (including Hadley) suggest you should for clarity. I personally recommend against it because the R profiler doesn't resolve function names of the style package::method, which makes it harder to optimize code (there are workarounds that can meet both objectives).

Note you should absolutely use @import and not rely solely on :: (you will also have to update the DESCRIPTION file). Failing to do so will prevent R from being aware of the dependency. I'm pretty sure R CMD check yells about this.

BrodieG
  • 51,669
  • 9
  • 93
  • 146
  • Addendum: another important reason to use `@import` is that it forces a load-time (rather than run-time) dependency. This can be important if the imported package has an `.onLoad()` hook which must be executed up front. – MichaelChirico Jul 03 '23 at 17:59