tl;dr:
R CMD check
complains when I implement a generic for the S3 class<-
, because it thinks that the function is a replacement function with incorrect arguments.
I need to define a set of S3 generics to walk an AST of unevaluated R expressions.
For demonstration purposes, consider the following S3 generic and its methods:
walk = function (x) UseMethod('walk')
walk.default = function (x) message('default')
walk.name = function (x) message('name')
walk.call = function (x) message('call')
This works fine:
tests = alist('2', c, f(1))
invisible(lapply(tests, walk))
default
name
call
However, there are quite a few call expressions whose S3 class isn’t call
; for example:
tests2 = alist(for (x in y) ., if (.) ., x <- .)
invisible(lapply(tests2, walk))
default
default
default
… oops. What I want for these expressions to be treated as calls. I can do this by adding more methods:
walk.for = function (x) message('for')
walk.if = function (x) message('if')
`walk.<-` = function (x) message('<-')
# … and so on for other syntax constructs.
And now I’m getting the expected result when calling walk
:
for
if
<-
However, this code is part of a package, and R CMD check
complains about the definition of walk.<-
because it thinks that the function is a replacement function:
W checking replacement functions ... ‘walk.<-’ The argument of a replacement function which corresponds to the right hand side must be named ‘value’.
And I understand why I’m getting the warning (indeed, this looks like a botched attempt at defining a replacement function for walk.
). But of course this isn’t a replacement function, so the warning is a false positive. So how am I supposed to implement an S3 generic for the class <-
unambiguously? Or is this a bug in R CMD check
? Can I get rid of the warning? I can’t add additional arguments to the functions.
Writing R Extensions, section 1.5.2 Registering S3 methods provides a workaround solution:
It is possible to specify a third argument to
S3method
, the function to be used as the method, for exampleS3method(print, check_so_symbols, .print.via.format)
when
print.check_so_symbols
is not needed.
This means that, instead of defining `walk.<-`
, we do the following:
R/‹some-file›.r
:walk_assign = function (x) message('<-')
(Unlike for other S3 methods, the function name is unimportant.)
NAMESPACE
declaration:S3method(walk, '<-', walk_assign)
This effectively does the same as calling .S3method('walk', '<-', \(x) message('<-'))
inside a regular R script. Interestingly, .S3method()
also seems to work inside a package (instead of the above NAMESPACE
-based solution), but the function’s documentation says not to use it in packages so this might break in the future.
Of course an arguably simpler solution, as commented by Allan Cameron, is to fuse the <-
case with default
and using if
inside walk.default
to disambiguate:
walk.default = function (x) {
if (inherits(x, '<-')) {
message('<-')
} else {
message('default')
}
}
However, personally I prefer not to mix S3 dispatch with if
.