3

I have a class myclass in an R package for which I would like to define a method as.raw, so of the same name as the primitive function as.raw(). If constructor, generic and method are defined as follows...

new_obj <- function(n) structure(n, class = "myclass") # constructor
as.raw <- function(obj) UseMethod("as.raw") # generic
as.raw.myclass <- function(obj) obj + 1 # method (dummy example here)

... then R CMD check leads to:

Warning: declared S3 method 'as.raw.myclass' not found
See section ‘Generic functions and methods’ in the ‘Writing R
Extensions’ manual.

If the generic is as_raw instead of as.raw, then there's no problem, so I assume this comes from the fact that the primitive function as.raw already exists. Is it possible to 'overload' as.raw by defining it as a generic (or would one necessarily need to use a different name?)?

Update: NAMESPACE contains

export("as.raw") # export the generic
S3method("as.raw", "myclass") # export the method

This seems somewhat related, but dimnames there is a generic and so there is a solution (just don't define your own generic), whereas above it is unclear (to me) what the solution is.

Marius Hofert
  • 6,546
  • 10
  • 48
  • 102
  • Possible duplicate of this question: https://stackoverflow.com/questions/59056308/make-a-existing-function-generic or https://stackoverflow.com/questions/42986174/cant-fix-warning-when-redefining-log-as-a-generic. You'll need to shadow the base function since it's not generic. – MrFlick Dec 28 '20 at 02:50
  • Thanks, that's interesting. I tried both versions, first `setGeneric("as.raw")`, then defining `as.raw.default <- function(x) base::as.raw(x)` as suggested in the link you posted. I still get the same warning. Could it be that R gets confused about the the two dots in `as.raw.myclass` and thus doesn't find the method? – Marius Hofert Dec 28 '20 at 03:02
  • Are you exporting these function? What does your NAMESPACE file look like? The two dots shouldn't matter. – MrFlick Dec 28 '20 at 03:04
  • I updated the post to show what `NAMESPACE` contains. – Marius Hofert Dec 28 '20 at 03:07
  • If I also export the default method via `S3method("as.raw", "default")` as in the second link you posted, I obtain the warning twice, once for `as.raw.myclass` and once for `as.raw.default`. – Marius Hofert Dec 28 '20 at 03:22
  • OK. This does appear to be weirder than I thought. The method seems to work for `as.name` and `as.roman` for example, but not for `as.raw` or `as.call` which is interesting because the latter are both primitive functions. – MrFlick Dec 28 '20 at 04:02
  • ... I'm glad you seem to be able to reproduce this. – Marius Hofert Dec 28 '20 at 04:09

1 Answers1

2

The problem here appears to be that as.raw is a primitive function (is.primitive(as.raw)). From the ?setGeneric help page, it says

A number of the basic R functions are specially implemented as primitive functions, to be evaluated directly in the underlying C code rather than by evaluating an R language definition. Most have implicit generics (see implicitGeneric), and become generic as soon as methods (including group methods) are defined on them.

And according to the ?InternalMethods help page, as.raw is one of these primitive generics. So in this case, you just need to export the S3method. And you want to make sure your function signature matches the signature of the existing primitive function.

So if I have the following R code

new_obj <- function(n) structure(n, class = "myclass")
as.raw.myclass <- function(x) x + 1

and a NAMESPACE file of

S3method(as.raw,myclass)
export(new_obj)

Then this passes the package checks for me (on R 4.0.2). And I can run the code with

as.raw(new_obj(4))
# [1] 5
# attr(,"class")
# [1] "myclass"

So in this particular case, you need to leave the as.raw <- function(obj) UseMethod("as.raw") part out.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • ... and I had tried that but missed that then the argument of the method has to match the one of `as.raw`, so `x` instead of `obj`. Thanks, it now works. – Marius Hofert Dec 28 '20 at 04:35