I have a small S4 class for which I've written a page grouping many of the accessors and replacement functions together. I would be interested in people comments on the approach I've taken. The code has a couple of decisions for which I could imagine alternatives. First, even simple get/set operations on class elements are wrapped in functions. I suppose I could just use myinstance at slot to do some of these operations, though that is considered bad style in more traditional OO contexts. Second, even though the functions are tied to the class, I've defined them as free functions rather than methods. I suppose I could create a generic that would reject most arguments, and then make methods appropriately. For the documentation, I've created a single page that groups many of the functions together. This is a bit awkward, since the return values are necessarily the same. Things are worse for replacement functions; as I understand it, they must use "value" for their final argument, but the value has different meanings and types in different contexts. Any suggestions or comments? I've attached the .Rd file in case more specifics would help. -- Ross Boylan wk: (415) 514-8146 185 Berry St #5700 ross at biostat.ucsf.edu Dept of Epidemiology and Biostatistics fax: (415) 514-8150 University of California, San Francisco San Francisco, CA 94107-1739 hm: (415) 550-1062 -------------- next part -------------- \name{runTime-accessors} \alias{startTime} \alias{endTime} \alias{wallTime} \alias{waitTime} \alias{cpuTime} \alias{mpirank} \alias{mpirank<-} \alias{remoteTime<-} \title{ Accessors for runTime class} \description{ Set and get runTime related information. } \usage{ startTime(runTime) endTime(runTime) wallTime(runTime) waitTime(runTime) cpuTime(runTime) mpirank(runTime) mpirank(runTime) <- value remoteTime(runTime) <- value } %- maybe also 'usage' for other objects documented here. \arguments{ \item{runTime}{a \code{runTime} object} \item{value}{for \code{mpirank}, the MPI rank of the associated job. For \code{remoteTime}, a vector of statistics from the remote processor: user cpu, system cpu, wall clock time for main job, wall clock time waiting for the root process.} } \details{ All times are measured from start of job. The sequence of events is that the job is created locally, started remotely, finished remotely, and completed locally. Scheduling and transmission delays may occur. \item{startTime}{When the job was created, locally.} \item{endTime}{When job finished locally.} \item{wallTime}{How many seconds between local start and completion.} \item{cpuTime}{Remote cpu seconds used, both system and user.} \item{waitTime}{Remote seconds waiting for response from the local system after the remote computation finished.} \item{mpirank}{The rank of the execution unit that handled the remote computation.} } \value{ Generally seconds, at a system-dependent resolution. \code{mpirank} is an integer. Replacement functions return the \code{runTime} object itself. } \author{Ross Boylan} \note{Clients that use replacement functions should respect the semantics above. } \seealso{\code{\link{runTime-class}}} \keyword{programming} \keyword{environment}
On Tue, 2006-09-26 at 00:20 +0000, Ross Boylan wrote:> I have a small S4 class for which I've written a page grouping many of > the accessors and replacement functions together. I would be interested > in people comments on the approach I've taken. > > The code has a couple of decisions for which I could imagine > alternatives. First, even simple get/set operations on class elements > are wrapped in functions. I suppose I could just use myinstance at slot to > do some of these operations, though that is considered bad style in more > traditional OO contexts. > > Second, even though the functions are tied to the class, I've defined > them as free functions rather than methods. I suppose I could create a > generic that would reject most arguments, and then make methods > appropriately. > > For the documentation, I've created a single page that groups many of > the functions together. This is a bit awkward, since the return values > areNOT> necessarily the same. Things are worse for replacement functions; > as I understand it, they must use "value" for their final argument, but > the value has different meanings and types in different contexts. > > Any suggestions or comments? > > I've attached the .Rd file in case more specifics would help.Sorry!
Ross Boylan <ross at biostat.ucsf.edu> writes:> The code has a couple of decisions for which I could imagine > alternatives. First, even simple get/set operations on class elements > are wrapped in functions. I suppose I could just use myinstance at slot to > do some of these operations, though that is considered bad style in more > traditional OO contexts.I like the get/set approach as opposed to using '@'. As long as users don't use '@' you have a fair amount of flexibility to redesign/refactor your code.> Second, even though the functions are tied to the class, I've defined > them as free functions rather than methods. I suppose I could create a > generic that would reject most arguments, and then make methods > appropriately.If anyone else is going to extend your classes, then you are doing them a disservice by not making these proper methods. It means that you can control what happens when they are called on a subclass.> For the documentation, I've created a single page that groups many of > the functions together. This is a bit awkward, since the return values > are necessarily the same. Things are worse for replacement functions; > as I understand it, they must use "value" for their final argument, but > the value has different meanings and types in different contexts. > > Any suggestions or comments?For accessors, I like to document them in the methods section of the class documentation. + seth -- Seth Falcon | Computational Biology | Fred Hutchinson Cancer Research Center http://bioconductor.org
Ross Boylan <ross at biostat.ucsf.edu> writes:> Did you want this offlist? I'm happy keeping it on the list.No, I accidentally responded privately and I believe I already resent my reply to the list. Sorry about that. I've cc'd the list for this response.>> If anyone else is going to extend your classes, then you are doing >> them a disservice by not making these proper methods. It means that >> you can control what happens when they are called on a subclass.> My style has been to define a function, and then use setMethod if I want > to redefine it for an extension. That way the original version becomes > the generic. > > So I don't see what I'm doing as being a barrier to adding methods. Am > I missing something?You are not, but someone else might be: suppose you release your code and I would like to extend it. I am stuck until you decide to make generics.> Originally I tried defining the original using setMethod, but this > generates a complaint about a missing function; that's one reason I fell > into this style.You have to create the generic first if it doesn't already exist: setGeneric("foo", function(x) standardGeneric("foo"))>> For accessors, I like to document them in the methods section of the >> class documentation.> This is for accessors that really are methods, not my fake > function-based accessors, right?Which might be a further argument not to have the distinction in the first place ;-) To me, simple accessors are best documented with the class. If I have an instance, I will read help on it and find out what I can do with it.> If you use foo as an accessor method, where do you define the associated > function (i.e., \alias{foo})? I believe such a definition is expected by > R CMD check and is desirable for users looking for help on foo (?foo) > without paying attention to the fact it's a method.Yes you need an alias for the _generic_ function. You can either add the alias to the class man page where one of its methods is documented or you can have separate man pages for the generics. This is painful. S4 documentation, in general, is rather difficult and IMO this is in part a consequence of the more general (read more powerful) generic function based system. IOW, I think these are good questions. They are ones that I struggle with and do not know of any truly satisfying answers. Best, + seth -- Seth Falcon | Computational Biology | Fred Hutchinson Cancer Research Center http://bioconductor.org