How to export S3 method so it is available in namespace?
Asked Answered
S

4

24

I am creating a package and for S3 methods I export them using

##' @method predict myclass
##' @export
predict.myclass <- function(object,...) { }

Now when I load the package, then predict works on object of the class myclass, but function predict.myclass is not exported. In the NAMESPACE I only get the entry S3method(predict,myclass). So is there a way to export predict.myclass too, so that user will get the code of predict.myclass when he(she) writes predict.myclass in the console?

Scission answered 29/8, 2013 at 13:36 Comment(0)
S
19

If you would like to have both an S3method and export directive in your NAMESPACE in an automated way with roxygen2, you can simply add an extra @export tag that is more explicit.

To illustrate, I created a dummy package, exportTest. This package has only one file in the R/ subdirectory, print.foo.R:

#' Print method for "foo" class
#'
#' @param x An object of class "foo"
#' @param ... Other arguments passed to or from other methods
#'
#' @export print.foo
#' @export
print.foo <- function(x, ...) {
    cat("This is just a dummy function.\n")
}

After document()ing, I have the following NAMESPACE:

# Generated by roxygen2: do not edit by hand

S3method(print,foo)
export(print.foo)

I got this idea from Hadley's advice on exporting a non-S3-method function with a . in the name. If you use

#' @export function.name

it explicitly uses an export() directive for certain with the provided function.name. Then I tested whether you could combine it with a more ambiguous @export tag to also generate an S3method() directive, and voila! It works.

However, I will note that to my knowledge, being able to do this for certain isn't documented anywhere, so it's possible to stop working at some point, perhaps even without warning. If this is functionality that you want to ensure exists and/or have documented somewhere, I would suggest opening an issue at their GitHub repo.

Sacrifice answered 27/6, 2020 at 11:50 Comment(0)
L
12

My answer is "don't do that". The user can methods(predict); getAnywhere('predict.myclass') or mypackage:::predict.myclass. There's a learning curve for the user, but mastering this with your method helps the user navigate all methods. Reasons not to export the method are that it isn't meant to be invoked directly, and it clutters the search path with unnecessary symbols (every symbol typed at the prompt, e.g., ls(), has to be found by looking through objects on all environments returned by search(), and user packages like yours stand between the start of the search and name resolution of these commonly used functions).

Levison answered 29/8, 2013 at 13:43 Comment(6)
OK, so here is another question #18514107. I thought that I solve my problem with additional export.Scission
Are you sure ? According to Hadley, every S3 method must be exported. Maybe I misunderstand your answer.Arabela
@StéphaneLaurent the OP already has S3method(predict, myclass), which is necessary and sufficient; maybe it's what Hadley means by 'must be exported'. It is neither necessary nor recommended to export(predict.myclass).Levison
Who says it is not recommended? If it is not recommended, why is print.data.frame exported?Bresee
@Bresee I guess I said it was not recommended? ;) print.data.frame is defined in the base package, where there is no namespace so no option not to export it.Levison
@MartinMorgan uh, OK, I didn't realize that :->Bresee
S
4

Just for those late to the party, in the newer versions of roxygen (=> 3.0), the @export tag will work automatically how to deal with methods.

From the Generating Rd files vignette:

S3 generics are regular functions, so document them as such. S3 classes have no formal definition, so document the constructor function. It is your choice whether or not to document S3 methods. You don’t need to document methods for simple generics like print(). If your method is more complicated, you should document it so people know what the parameters do. In base R, you can find documentation for more complex methods like predict.lm(), predict.glm(), and anova.glm().

Older versions of roxygen required explicit @method generic class tags for all S3 methods. From 3.0.0 this is no longer needed as and roxygen2 will figure it out automatically. If you are upgrading, make sure to remove these old tags. Automatic method detection will only fail if the generic and class are ambiguous. For example is all.equal.data.frame() the equal.data.frame method for all, or the data.frame method for all.equal?. If this happens, you can disambiguate with (e.g.) @method all.equal data.frame.

Sitwell answered 16/4, 2018 at 20:6 Comment(0)
B
3

I completely concur that importing an S3 method is desirable in some cases, and I am not aware of any autoritative source claiming that doing so is not recommended. Even the base package exports method like print.data.frame.

That said, I have not found a reasonable solution with roxygen2 other than modifying NAMESPACE file by hand and adding the line

export(print.myClass)

The alternative is to duplicate the function:

 print_myClass <- function(x, ...) print.myClass(x, ...)

Both of these solutions have drawback. The former has the obvious drawback of manually editing NAMESPACE, the latter introduces confusion.

Bresee answered 27/6, 2020 at 9:8 Comment(1)
‘base’ exports all names for historical reasons, not because it’s an intentional decision to export a particular name. By contrast, other core R packages do not export S3 method implementations.Filip

© 2022 - 2024 — McMap. All rights reserved.