0

In a package, I have a function foo that returns an object of class "foo". I also have a plot method for class "foo".

#' Create a "foo" object
#'
#' @param x An \R object.
#' 
#' @return
#' A "foo" object.
#'
#' @examples
#' foo_object <- foo(1)
#' plot.foo(foo_object)
#'
#' @export
foo <- function(x) {
    structure(x, class = "foo")
}

#' @export
#' @importFrom graphics plot
plot.foo <- function(x, ...) {
    class(x) <- setdiff(class(x), "foo")
    plot(x)
    invisible(NULL)
}

I can evaluate the example code without issue after I load the package with devtools::load_all. However, devtools::check complains:

Error in plot.foo(foo_out) : could not find function "plot.foo"
  Execution halted

It seems that my R session knows about plot.foo, but not devtools::check. What is going on?


Edit: To clarify, devtools::check passes when I replace the call plot.foo(foo_object) under @examples with plot(foo_object). That doesn't surprise me, and users should call the generic anyway. My question remains: why is devtools::check unable to find plot.foo, given that I have used the @export tag and S3method(plot, foo) appears in NAMESPACE after devtools::document?

Mikael Jagan
  • 9,012
  • 2
  • 17
  • 48
  • All those `Roxygen2` comments are ignored by R. What matters is what is in your `NAMESPACE` file. Show us that. I'd guess you don't have `S3method(plot, foo)` in there. Figure out how to get it there, and you'll be fine. – user2554330 Aug 19 '20 at 22:24
  • My `NAMESPACE` file includes both `export(foo)` and `S3method(plot,foo)`. – Mikael Jagan Aug 19 '20 at 22:37
  • `export(foo)` is irrelevant, because the error message is about a call to `plot.foo`. I think you are misleading us about what you've really done. Post real code, not fake. – user2554330 Aug 20 '20 at 00:17
  • Yes, it was sloppy of me to use fake code without properly checking that it reproduces my problem... I will delete this question shortly, since I've managed to resolve the issue with the *real* code. – Mikael Jagan Aug 20 '20 at 02:41
  • OK, I've fixed the question to make it instructive, instead of deleting it. – Mikael Jagan Aug 20 '20 at 03:32
  • 1
    Normally when you export a function as an S3 method, you do not actually export the implementation function Itself. It remains unexported in the package so you can’t call it directory. If you must export it as well, add `@export plot.foo` above the other export for the plot.foo function. See: https://stackoverflow.com/questions/18512528/how-to-export-s3-method-so-it-is-available-in-namespace – MrFlick Aug 20 '20 at 05:47

1 Answers1

2

If you want to be able to call plot.foo directly, then you will need to explicitly export that version as well. By convention, usually you do not export class-specific implemenations of generic functions from your package. Normally you just declare that the S3 method exists and leave the function unexported. Like if you call methods(plot) you'll see a bunch with asterisks which means they are unexpected and are not meant to be called directly. If you do for some reason want to export it as a separate function, you can add an additional export statement. For example

#' @rdname foo-methods
#' @export plot.foo
#' @export
#' @importFrom graphics plot
plot.foo <- function(x, ...) {
  class(x) <- setdiff(class(x), "foo")
  plot(x)
  invisible(NULL)
}
MrFlick
  • 195,160
  • 17
  • 277
  • 295