Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

S4 missing or NULL arguments to methods?

Tags:

r

s4

Is there a way to define what happens when the argument of a method is missing or NULL?

In the example below, I'd like to call the same function no matter whether I type foo() or foo(NULL). Of course I know that I can have a method setMethod("foo","NULL",function(x) foo()) but that that's code replication and a possible source of mistakes.

Thanks!

setGeneric("foo",function(x) standardGeneric("foo"))
setMethod("foo","numeric",function(x) "numeric")
setMethod("foo","NULL",function(x) "NULL")
setMethod("foo","missing",function(x) "missing")

R> foo(1)
[1] "numeric"
R> foo()
[1] "missing"
R> foo(NULL)
[1] "NULL"
like image 570
Florian Bw Avatar asked Dec 14 '10 14:12

Florian Bw


3 Answers

Almost exactly three years late to the party, but you really want setClassUnion:

> setClassUnion("missingOrNULL", c("missing", "NULL"))
> setGeneric("foo",function(x) standardGeneric("foo"))
> setMethod("foo","numeric",function(x) "numeric")
> setMethod("foo","missingOrNULL",function(x) "NULL")

> foo(1)
[1] "numeric"
> foo()
[1] "NULL"
> foo(NULL)
[1] "NULL"

setClassUnion creates a virtual class that is a superclass (parent) to the component classes, so then both children inherit from that class, which means you can dispatch the same function against each child.

like image 190
BrodieG Avatar answered Sep 20 '22 07:09

BrodieG


Using setMethod("foo","NULL",function(x) foo()) is not a code replication, since you don't replicate code but put a call to it. I would say it is a very good way of solving your problem.

like image 38
mbq Avatar answered Sep 22 '22 07:09

mbq


I guess the appropriate way is to use "ANY" in the signature:

setGeneric("foo",function(x) standardGeneric("foo"))
setMethod("foo","numeric",function(x) "numeric")
setMethod("foo","ANY",function(x) "ANY")

> foo(1)
[1] "numeric"

> foo()
[1] "ANY"

> foo(NULL)
[1] "ANY"

Make sure you specify every other possibility that you want taken care of, as "ANY" also takes all the rest that doesn't fit to the signature of another method.

If you have arguments that can be missing, you can just not specify them in the signature of setMethods and set a default in the generic. This is -in my humble view- a better design choice.

setGeneric("foo",function(x,y=NULL,...) {
        standardGeneric("foo")
    })

setMethod("foo",c("numeric","ANY"),function(x,y,...) {
            print(y)
    })
setMethod("foo",c("numeric","numeric"),function(x,y,...) {
            x + y
    })

> foo(1)
NULL

> foo(1,3)
[1] 4

> foo(1,NULL)
NULL

Now you can deal with the NULL cases in-code like you would do with the missing arguments.

On a sidenote: Now I added NULL as default value, but in many cases there are far more sensible choices for default values. Just remember that the setMethod takes the initial signature, and that when y is set as NULL, this is not replaced by the default.

eg:

setGeneric("bar",function(x,y=2,...) {
        standardGeneric("bar")
    })

setMethod("bar",c("numeric","ANY"),function(x,y,...) {
            x + y
    })
setMethod("bar",c("numeric","numeric"),function(x,y,...) {
            x - y
    })

> bar(1)
[1] 3

> bar(1,2)
[1] -1

> bar(1,NULL) # this y is not replaced with the default!
numeric(0)

DIRTY HACK :

I find the approach a bit awkward, but here is a dirty hack that sets all missing parameters to NULL :

setGeneric("foo",function(x,y,z) {
    pars <- names(formals(foo))
    for(i in pars){
        tryerr <- try(get(i),silent=T)
        if(is(tryerr,"try-error")){ assign(i,NULL)}
    }
    standardGeneric("foo")
}

Trying this, you get :

> foo(1)
[1] "numeric"

> foo(NULL)
[1] "NULL"

> foo()
[1] "NULL"

So you never dispatch to the missing any more. You can just forget about it. But this ain't the appropriate way of doing things...

like image 29
Joris Meys Avatar answered Sep 20 '22 07:09

Joris Meys