Skip to content

[R-pkg-devel] Exporting S3 methods for base generics

8 messages · Charles Determan, Joris Meys

#
Greetings R users,

I was wondering how others are exporting S3 methods in their packages when
the generic is in 'base'.  For example, let's say I want to export a new
pmax method.  The only way I have found to get this to work is by
redefining the function with 'UseMethod' and setting the default method.

#' @export
pmax <- function(...){ UseMethod("pmax") }
#' @export
pmax.default <- function(..., na.rm=FALSE){ base::pmax(..., na.rm=FALSE) }

setClass("myclass")

#' @export
pmax.myclass <- function(..., na.rm = FALSE){
    print('myclass pmax!')
}

Although this works, I get the 'warning'

The following objects are masked from 'package:base':

    pmax


I would like the package build and loading to be as clean as possible but
if this is acceptable and not considered a problem I will let it go.  It
just seems odd that one would to redefine a the generic when in states in
the docs for 'pmax' that it will also work on classed S3 objects but
perhaps I am reading this incorrectly.

Thanks,
Charles
#
Hi Charles,

if a generic exists already in the base, you only have to export the actual
S3 method. Your problem is that base::pmax() is not a generic S3 function.
So R gives you the correct warning: the S3 generic in your package will
always mask the base pmax function. And that's not really a problem,
especially since you ensured the base functionality with your default
method.

If you want to avoid that warning, use S4.

#' @rdname
setGeneric("pmax")

#' @rdname pmax
#' @method pmax myclass
#' @export
setMethod("pmax",
                  "myclass",
                  function(...){
                     # do some stuff
})

More information on how to deal with dots can be found on the help page
?dotsMethods.

If you have a generic in the base package (eg plot is such one), you only
define the method and use:

#' @export
plot.myclass <- function(x, y, ...){
   # do some more stuff
}

Cheers
Joris



On Mon, Jun 26, 2017 at 6:28 PM, Charles Determan <cdetermanjr at gmail.com>
wrote:

  
    
#
Thanks for the reply Joris, although I am not sure what I could be doing
wrong.  I implement exactly the lines you show and yet I just get the
following error when I call 'pmax' on the class.
Error in mmm < each :
  comparison (3) is possible only for atomic and list types
In addition: Warning message:
In is.na(mmm) : is.na() applied to non-(list or vector) of type 'S4'

Regards,
Charles
On Mon, Jun 26, 2017 at 12:10 PM, Joris Meys <Joris.Meys at ugent.be> wrote:

            

  
  
#
Hi Charles,

my mistake. I forgot that pmax has an extra argument na.rm. I'm surprised
you could define the method, as this normally should return an error from
conformMethod().

So:
#' @rdname pmax
setGeneric("pmax", signature = "...")

should work. I've used this myself in quite a number of packages.

Cheers
Joris

On Mon, Jun 26, 2017 at 7:20 PM, Charles Determan <cdetermanjr at gmail.com>
wrote:

  
    
#
Could you point to one of these packages you refer to?  I'm still having
problems and not sure why at the moment.

Thanks
On Mon, Jun 26, 2017 at 12:32 PM, Joris Meys <Joris.Meys at ugent.be> wrote:

            

  
  
#
Ah, I see now.  I came across this previous post (
http://r.789695.n4.nabble.com/override-pmin-pmax-for-my-own-matrix-td4715903.html)
which mentioned the caveat that all the elements passed to ... must be the
same.  When I pass two of the same class it works but I believe I will need
to go back to the S3 if I want different classes passed to the call.

Charles

On Mon, Jun 26, 2017 at 12:42 PM, Charles Determan <cdetermanjr at gmail.com>
wrote:

  
  
#
Hi Charles,

that particular construct (with the dots) I only have in proprietary
packages, so I can't share the code for those. If you have the package on a
repo on github, you can give me a link and a pointer as to what file you're
working on, and I can take a look.

You also find some nice examples on the help page of ?dotsMethods

Cheers
Joris

On Mon, Jun 26, 2017 at 7:42 PM, Charles Determan <cdetermanjr at gmail.com>
wrote:

  
    
#
Ah, good point :-)  You can avoid that by using inheritance, but now you're
on a route to refactoring your entire package actually. See also this
question on stackoverflow for a nice example of how this can be done:

https://stackoverflow.com/questions/26963900/generalizing-three-dots-argument-dispatch-s4-methods-for-argument-set-i

If neither class can inherit from the other in a logical way, you could
define a virtual "superclass" they both inherit from, and define the pmax
method for that virtual class. In R you do this using setClassUnion(), see
also ?setClassUnion and the section on virtual classes in ?setClass.

Hope this helps
Cheers
Joris

On Mon, Jun 26, 2017 at 7:56 PM, Charles Determan <cdetermanjr at gmail.com>
wrote: