How can I avoid the classic Error: argument "<argname>" is missing, with no default
error (see example below) when explicitly dispatching argument values to subsequent S4 methods in a given S4 method.
Example
Big picture
- Whe have a method
foo()
that calls methodbar()
. - Both methods depend on arguments
x
andy
. - Method
foo()
dispatches argumentsx
andy
tobar()
in an explicit way:bar(x=x, y=y)
.
Now, the crucial point here is that I don't want foo()
to care if any or all of the arguments that are passed along to bar()
are missing.
Generic methods
setGeneric(
name="foo",
signature=c("x", "y"),
def=function(x, y, ...) {
standardGeneric("foo")
}
)
setGeneric(
name="bar",
signature=c("x", "y"),
def=function(x, y, ...) {
standardGeneric("bar")
}
)
Methods for bar()
setMethod(
f="bar",
signature=signature(x="missing", y="missing"),
definition=function(x, y, ...) {
print("Doing what I'm supposed to do when both args are missing")
return(NULL)
}
)
setMethod(
f="bar",
signature=signature(x="ANY", y="missing"),
definition=function(x, y, ...) {
message("'y' is missing, but I can give you 'x':")
print(x)
return(NULL)
}
)
setMethod(
f="bar",
signature=signature(x="missing", y="ANY"),
definition=function(x, y, ...) {
message("'x' is missing, but I can give you 'y':")
print(y)
return(NULL)
}
)
setMethod(
f="bar",
signature=signature(x="ANY", y="ANY"),
definition=function(x, y, ...) {
message("x:")
print(x)
message("y:")
print(y)
return(NULL)
}
)
Method for foo()
As mentioned above, I don't want foo()
to care if any or all of the arguments that are passed along to bar()
are missing. It is just supposed to pass everything along to bar()
in an explicit manner:
setMethod(
f="foo",
signature=signature(x="ANY", y="ANY"),
definition=function(x, y, ...) {
bar(x=x, y=y)
}
)
The method def might look good on first sight, but it will fail if either x
or y
are missing when calling it:
> foo(x="Hello", y="World!")
x:
[1] "Hello"
y:
[1] "World!"
NULL
> foo(x="Hello")
Error in bar(x = x, y = y) :
error in evaluating the argument 'y' in selecting a method for function 'bar': Error: argument "y" is missing, with no default
> foo()
Error in bar(x = x, y = y) :
error in evaluating the argument 'x' in selecting a method for function 'bar': Error: argument "x" is missing, with no default
Workaround
This is the only workaround that I could come up with so far:
setMethod(
f="foo",
signature=signature(x="ANY", y="ANY"),
definition=function(x, y, ...) {
if (missing(x) && missing(y)) {
bar()
} else if (missing(x)) {
bar(y=y)
} else if (missing(y)) {
bar(x=x)
} else {
bar(x=x, y=y)
}
}
)
> foo(x="Hello", y="World!")
x:
[1] "Hello"
y:
[1] "World!"
NULL
> foo(x="Hello")
'y' is missing, but I can give you 'x':
[1] "Hello"
NULL
> foo(y="World!")
'x' is missing, but I can give you 'y':
[1] "World!"
NULL
> foo()
[1] "Doing what I'm supposed to do when both args are missing"
NULL
It works, but I don't really like it because of all the if ... else
statements. The whole "if-else logic" already went in the specification of the various methods for bar()
. After all, that's the whole point of having a method dispatcher in the first place, right? Hence I would consider the statements as "undesired work" and I'm looking for a better way.
One could of course resort to to using NULL
as default value for all "critical" arguments, but I would like to rely on missing()
instead of is.null()
in my functions as much as possible.