Skip to content

Could you please add "time<-" as a generic function in the 'stats' package ?

6 messages · Yohan Chalabi, John Chambers

#
Dear R developers,

As you might have noticed, recent changes in R-dev will not allow the  
definition of S3 methods with S4 classes.

But until now, we have defined "time<-" in our 'timeSeries' package as  
an S3 generic because other packages are using the same function.  
Indeed, if we had defined it as an S4 generic, the other packages  
would not coexist well with ours.

Another package might overwrite the generic and its methods when both  
packages are loaded.

In my understanding the only way to avoid this problem is to add

`time<-`
function (x, value)
{
    UseMethod("time<-")
}

in the 'stats' package.

As a wish for the forthcoming R version I would like to ask you if you  
could add "time<-" as a generic function in the 'stats' package to  
prevent conflicts and to ensure that packages continue to work well  
together.

Thank you in advance for your feedback!
#
Whatever one wants for an S3 generic, it's not needed to do what, 
presumably, you want here.

And for sure it is no excuse for S3 methods for S4 classes.

Back to basics: To write S4 methods for an existing function, the clean 
and simple way is usually:

setGeneric("time<-")

If your package depends on one that has S3  methods for this function, 
there will be a version of the function imported into your namespace.  
That function will then be the default method.

Presumably you want to ensure that S3 methods, for S3 classes, are still 
dispatched.  Quite reasonable and it should follow from the call to 
setGeneric.

If you wanted to have your own S3 methods or if you weren't willing to 
assume an S3 generic imported, you could do a 2-line version:

R(r48103)> `time<-` <- function(x, value) UseMethod("time<-")
R(r48103)> setGeneric("time<-")
[1] "time<-"
R(r48103)> showMethods("time<-", include = TRUE)
Function: time<- (package .GlobalEnv)
x="ANY"
function (x, value)
UseMethod("time<-")

As a postscript, here is the current plan, not yet committed, pending 
some more testing:
  - the bad methods will be allowed
  - warnings when a class is defined with such methods for a superclass
  - probably some other warnings, but not for an ordinary call to the 
method (it's the MISSING calls to the method that are the disaster).

More later,
  John
Yohan Chalabi wrote:
#
JC> Whatever one wants for an S3 generic, it's not needed to do what,
   JC> presumably, you want here.
   JC>
   JC> And for sure it is no excuse for S3 methods for S4 classes.
   JC>
   JC> Back to basics: To write S4 methods for an existing function, the clean
   JC> and simple way is usually:
   JC>
   JC> setGeneric("time<-")
   JC>
   JC> If your package depends on one that has S3  methods for this function,
   JC> there will be a version of the function imported into your namespace.
   JC> That function will then be the default method.
   JC>
   JC> Presumably you want to ensure that S3 methods, for S3 classes, are still
   JC> dispatched.  Quite reasonable and it should follow from the call to
   JC> setGeneric.
   JC>
   JC> If you wanted to have your own S3 methods or if you weren't willing to
   JC> assume an S3 generic imported, you could do a 2-line version:
   JC>
   JC> R(r48103)> `time<-` <- function(x, value) UseMethod("time<-")
   JC> R(r48103)> setGeneric("time<-")
   JC> [1] "time<-"
   JC> R(r48103)> showMethods("time<-", include = TRUE)
   JC> Function: time<- (package .GlobalEnv)
   JC> x="ANY"
   JC> function (x, value)
   JC> UseMethod("time<-")

In my opinion you example only works in '.GlobalEnv' environement, but
does not work when implemented in a package.

I wrote a small package to illustrate the problem.

## R code
oldDir <- getwd()
setwd(tempdir())

url <- "http://nic.phys.ethz.ch/~chalabi/timeProb_0.001.tar.gz"
download.file(url, "timeProb_0.001.tar.gz")
install.packages("timeProb_0.001.tar.gz", repos = NULL)

# you also need to install another package which uses `time<-`.
# for example zoo
if(!require(zoo))
    install.packages("zoo")

# Now we quit and start a new R session
setwd(oldDir)
q()

# new R

# we first load timeProb
library(timeProb)

# and then run the demo
demo(timeProb)

# now we quit and start again a new session
q()

# new R

# but now we first load zoo and after timeProb
library(zoo)
library(timeProb)

# and run again the demo
demo(timeProb)

## end R code

As far as I understand it, two packages can not coexist if one of
them defines an S3 generic and the other one defines an S4 generic
with the same name.

Or am I missing an option when defining the S4 generic?

   JC>
   JC> As a postscript, here is the current plan, not yet committed, pending
   JC> some more testing:
   JC>   - the bad methods will be allowed
   JC>   - warnings when a class is defined with such methods for a superclass
   JC>   - probably some other warnings, but not for an ordinary call to the
   JC> method (it's the MISSING calls to the method that are the disaster).
   JC>

Thanks for giving more information concerning the current plan.

   JC> More later,
   JC>   John
   JC>


regards,
Yohan

--
PhD student
Swiss Federal Institute of Technology
Zurich

www.ethz.ch
3 days later
#
JC> The problems are related to masking objects (in this case ) in
   JC> the search list, not especially related to methods.
   JC>
   JC> It was in order to get around such problems that NAMESPACE
   JC> was added to
   JC> R.  You should use it, but it applies to evaluating calls
   JC> to functions
   JC> in the package, by avoiding the dependency on the order of
   JC> packages in
   JC> the search list.  To ensure correct results, you need to call a
   JC> function from your package (i.e., one that is not masked).  The
   JC> computations in the function will see what has been imported
   JC> into the
   JC> namespace.
   JC>
   JC> For example, if you do the following:
   JC>
   JC> 1.  add a NAMESPACE file, for example containing:
   JC>
   JC> import(stats)
   JC> import(zoo)
   JC> exportPattern(^[a-zA-Z])
   JC>
   JC> 2.  Do the computations in a function in your package,
   JC> say doDemo(),
   JC> with a few show(time()) lines added to print things.
   JC>
   JC> 3.  With the import(zoo), no need to define as an S3 generic.
   JC>
   JC> Then things behave with or without zoo attached, because the
   JC> computations are defined by your namespace.


Thank you for your responses.

'timeSeries' and 'zoo' both have functionality for time series
management. Although they have similar concepts, they are intrinsically
different; the former package uses S4 classes and the latter S3 classes.

Until now both packages have been able to coexist and have been  
independent from each other.

As I mentioned in my previous post, both packages define methods to  
extract timestamps of their respective classes with the function  
'time' .

I agree with you that if we had used a function name and its  
assignment version defined in 'zoo', we should import it from their  
namespace. But in this case, 'time<-' is the natural extension of a  
function already present in a base package.

Until now we defined the S3 generic 'time<-' so that both packages  
could coexist without needing to import the function from the  
namespace of the other. But this workaround won't work anymore if we  
define an S4 generic.

We are thus asking the R developers if they could add 'time<-'  as a  
generic in 'stats' because it is the natural extension of an existing  
function. This will ensure that packages can continue to coexist and  
remain independent.

Best regards,
Yohan