From sfalcon at fhcrc.org  Tue Feb 14 17:22:04 2006
From: sfalcon at fhcrc.org (Seth Falcon)
Date: Tue, 14 Feb 2006 08:22:04 -0800
Subject: [Rd] S4 classes and methods with optional arguments
In-Reply-To: <20060214154423.GA6722@essek.stat-math.wu-wien.ac.at> (Josef
	Leydold's message of "Tue, 14 Feb 2006 16:44:23 +0100")
References: <mailman.7.1139914801.6090.r-devel@r-project.org>
	<20060214154423.GA6722@essek.stat-math.wu-wien.ac.at>
Message-ID: <m2u0b152s3.fsf@ziti.local>

Hi Josef,

On 14 Feb 2006, leydold at statistik.wu-wien.ac.at wrote:
> I have used a construct to allow optional arguments:
>
> if(!isGeneric("rstream.sample")) setGeneric("rstream.sample",
> function(stream,...) standardGeneric("rstream.sample"))

First, a question:
  Is this idiom of testing for the generic before defining it still
  "recommended"?  It seems to me that one should either define one's
  own generic in the package namespace or define a method for a
  *particular* generic defined elsewhere.  Otherwise, one could end up
  defining a method for the wrong generic.

> setMethod("rstream.sample", c("rstream","numeric"),
> function(stream,n=1) { ... [ code ] ... } )

This will work if you remove the second arg in the signature.  That
is,

  setMethod("rstream.sample", signature(stream="rstream"),
            function(strea, n=1) { ... })

Putting an arg in the signature means dispatching on that arg.  You
cannot dispatch on an arg that is not named in the definition of the
generic.

> however, with R-devel the above construct does not work any more,
> due to more stringent checkings. It can be fixed by replacing it by
>
> if(!isGeneric("rstream.sample")) setGeneric("rstream.sample",
> function(stream,n) standardGeneric("rstream.sample"))
>
> setMethod("rstream.sample", c("rstream","numeric"),
> function(stream,n=1) { ... [ code ] ... } )
>
> then rstream.sample(rs) does not work any more.
>
> Is there still a way to allow optional arguments for methods of S4
> classes?

Here's an approach that works for me:

1. You have to specify a default value to args *in the generic*.  This
   doesn't make a whole lot of sense to me, but it does seem to be
   needed.

   setGeneric("rstream.sample", 
              function(stream, n=0) standardGeneric("rstream.sample"))

2. Then define a method with a signature that matches the default
   case:

   setMethod("rstream.sample", signature(stream="rstream", n="missing"),
             function(stream, n=1) { ... })

   Note that you could also use signature(stream="rstream"), but then
   a call like rstream.sample(s, "foo") could match... Leaving out the
   arg is like saying n="ANY".

HTH,

+ seth



[Rd] S4 classes and methods with optional arguments
Seth Falcon