Rev 52856 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
\name{ReferenceClasses}\alias{ReferenceClasses}\alias{setRefClass}\alias{refClassFields}\alias{refClassMethods}\alias{refClassRepresentation-class}\alias{refClass-class}\alias{refObject-class}\alias{classMethodDef-class}\alias{SuperClassMethod-class}\alias{show,classMethodDef-method}\alias{show,refClassRepresentation-method}\title{Creating Classes With Fields Treated by Reference (OOP-style)}\description{A call to \code{setRefClass} defines an \R class withfields thatare accessed by reference and with class-based methods to be invoked on objectsfrom the class, in the style of languages such as Java andC++, commonly referred to as OOP languages.The application interface provided is designed to deal consistentlywith such classes whether implemented entirely in \R{} or through aninterface to another language.}\section{NOTE:}{The software described here is an initial version. The eventual goalis to support reference-style classes with software in \R itselfor using inter-system interfaces. The current implementation (\Rversion 2.12.0) is preliminary and subject to change, and currentlyincludes only the \R-only implementation. Developers are encouragedto experiment with the software, but the description here is more thanusually subject to change.A comment on terminology. Some terms are needed to distinguish theOOP-style structure from the similar function-based classes and methodsstructure. For the moment, the terms \dQuote{field} and\dQuote{class-based method} are used in distinction to \dQuote{slot}and \dQuote{method} for the usual S4 things.Other suggestions are welcome, but there are arguments for andagainst most obvious terminology borrowed from other languages. And,as mentioned below, even the term \dQuote{OOP} is potentially confusing.}\usage{setRefClass(Class, fieldClasses = , contains = , classMethods =,fieldPrototypes =, basicRefClass = "stdRefClass",refClassName = Class, where =, ...)refClassFields(Class)refClassMethods(Class)}\arguments{\item{Class}{character string name for the class.}\item{fieldClasses}{a named list of the fields, to be handled by reference semantics (seethe corresponding section below). Note that these are distinct fromthe slots, if any, in the object. Slots are, as always, handled bystandard \R{} object management.}\item{contains}{optional vector of superclasses for this class. If a superclass isalso a reference class, the fields and class-based methods will be inherited.}\item{classMethods}{a named list of function definitions that can be invoked on objectsfrom this class. See the section on class-based methods below, and inparticular the comments on accessor methods for fields.}\item{fieldPrototypes}{an optional named list of prototype objects for a subset of thefields. By default, each field will be initialized to the prototypeobject of the corresponding class.}\item{basicRefClass}{the name of a reference class from which the new class expects to inherit methodsto manage the reference semantics.The design goal is to define distinct implementations of referencethrough a set of basic reference classes.The default, \code{"stdRefClass"}, implements reference objectsentirely in \R{} (currently by using environments); other choices ofbasic reference class might use an interface to another language. Seethe section on inter-system interfaces.}\item{refClassName}{optionally, a name for the class to be used in interfacing to anotherlanguage, if for some reason that name is not suitable as an \R{}class name. By default, and usually, the name provided byargument \code{Class} is used.}\item{where}{the environment in which to store the class definition. Defaults tothe package namespace or environment for code that is part of an \R{}package, and to the global environment for code sourced directly atthe session top level.}\item{\dots}{other arguments to be passed to \code{\link{setClass}}.}}\section{Reference Objects}{Normal objects in \R are passed as arguments consistently withfunctional programming semantics; that is, changes made to an objectpassed as an argument are local to the function call. The object thatsupplied the argument is unchanged.Functional object semantics (sometimes called pass-by-value) aresuitable for many statistical computations and are implicit, forexample, in the basic \R software for fitting statistical models.In other situations, one would like all the code dealing with anobject to see the exact same content, so that changes made in anyfunction call would be reflected everywhere.This is often suitable if the object has some \dQuote{objective}reality, such as a window in a user interface.In addition, a number of languages, such as Java and C++ and manyothers, support a version of classes and methods assuming referencesemantics.The basic programming mechanism is to invoke a method on an object,different in several respects from a function call in \R.In the \R syntax we use for this operation, one invokes a method,\code{m1} say, on an object \code{x} by the expression\code{x$m1(...)}. It's not a logical necessity that such %$class/method systems use reference semantics, essentially all of themdo.In particular, they usually include methods that modify the content ofthe object.The goal of the software described here is to provide a uniformprogramming style in \R to deal with such software, whether it isimplemented directly in \R or through an interface to one of the otherlanguages.Several implications follow for the \R software.Methods in this model are associated with the object, or moreprecisely with the class of the object, as opposed to methods in afunction-based class/method system, which are fundamentally associatedwith the function (in \R, for example, a generic function in an \Rsession has a table of all its currently known methods).This relation of methods to objects is one reason such systems areoften referred to as Object Oriented Programming (OOP). While thereis nothing incorrect about the term, it has been the cause ofconfusion, particularly when programmers come to \R from one of theother systems.In a normal use of the words, the function-based class/method system in \Ris just as object-oriented as Java, say, if not more so.But the systems are fundamentally different.To avoid propagating the confusion, we will avoid the term OOPthroughout this document.If we need to make the distinction, we will refer to class-basedmethods versus function-based methods.}\section{Fields and Class-Based Methods}{All the widely used languages implementing class-based methods providefor named \dQuote{pieces} of an object.The term for these pieces varies with the language; in the \Rimplementations they will be called \emph{fields}.In many languages, a field is accessed by using the operator\code{"."}, but this is not available to us in \R because thecharacter can be part of a name (blame it on the Unix system whoseconvention of starting hidden files with \code{"."} was taken over inS).Instead, and rather than introduce yet another operator into thegrammar, we use the \code{$} operator, %$as has been done in a number of related \R packages, includinginter-system interfaces to languages such as Java and C++.A field is extracted by this operator and modified by the operatorappearing on the left of an assignment.In terms of implementation, any class representing reference objectsof this form will have or inherit methods for \code{"$"} and\code{"$<-"}.Objects from reference classes will inheritthese methods from a basic reference class, either using \R (thestandard being class \linkS4class{stdRefClass}) or interfacing toanother system.Similarly, a class-based method is invoked on an object using the \code{$}operator. To invoke a class-based method \code{undo} on an object \code{x},call \code{x$undo()}.}\section{Writing Class-Based Methods}{Class-based methods are functions supplied as elements of a named list asthe argument \code{classMethods} in a call to \code{setRefClass}.They are written as ordinary \R functions but have some specialfeatures and restrictions.The body of the function can contain calls to any other class-based method,including those inherited from other reference classes.Class-based methods also need to refer to fields in the object.A number ofclass-based systems recommend or enforce \emph{accessor methods}corresponding to each field.In the \R version presented here (and fairly often elsewhereas well), a field named \code{abc} of an object \code{x} would beextracted by \code{x$getAbc()} and assigned by\code{x$setAbc(value)}.Aside from other reasons, using accessor methods in the standard \Rimplementation allows class-based methods to be defined independently of theprecise mechanism currently used to implement fields (see theimplementation section below).A requirement for defining reference classes in \R is that methods beavailable to get any field and to set any field, although an errormay be generated if a field is defined to be read-only.For reference classes using the \linkS4class{stdRefClass} basicreference class, the use of accessor methods is optional.An alternative is to use the field name directly for getting the fieldand assignments of the field name using thenon-local assignment operator, \code{<<-}, to set the field. See thesecond version of class-based methods in the example below.Notice that non-local assignment is required: a local assignment withthe \code{<-} operator just creates a local object in the functioncall, as it would in any \R function.Direct access to fields in a class-based method usually gives a simpler and slightly fasterimplementation.However, using accessor methods is required if the referenceobject is a proxy for an object in another language.Using them in the \R-only case as well has the advantage of being compatiblewith the external case, so the same method definition applies to both.That's an important advantage in principle, becauseone of the main goals for the software described here is to provide aconsistent programming interface both for \R-only software and forinter-system interfaces.class-based methods should be kept simple; if they need to do somespecialized \R computation, put that into a separate, ordinaryfunction and write the method to call that.Specifically, methods can not use special features of theenclosing environment mechanism.methods can not themselves be generic functions; if you wantadditional function-based method dispatch, write a separate genericfunction and call that from the method.The implementation section explains the reason for these restrictions.The entire object can be referred to in a method by the reservedname \code{.self}, as shown in the \code{save=} method of theexample.The methods defined in the class include methods from superclasses, asdiscussed in the next section.}\section{Inheritance}{Reference classes inherit from other reference classes by using thestandard \R inheritance; that is, by including the superclasses in the\code{contains=} argument when creating the new class.Non-reference classes can also be included in the \code{contains=} argument. The class definitionmechanism treats reference and non-reference superclasses slightly differently.If the contained reference classes themselves have referencesuperclasses, these will be moved ahead of any non-referencesuperclasses in the class definition (otherwise the ordering ofsuperclasses may be ambiguous).The names of the reference superclasses are in slot\code{refSuperClasses} of the class definition.Class fields are inherited. Currently the new class definition canoverride any inherited fields: the previous field definition is nolonger available.Inherited methods are installed in the same way as directlyspecified methods.The code in a method can refer to inherited methods in the sameway as directly specified methods.A method may override a method of the same name in a superclass. Forthis purpose, a special method name will be provided, to be translatedinto a call to the superclass method overridden. This mechanism hasnot yet been implemented, but the current plan is that\code{callSuper(...)} will translate into a call to the superclassversion of the current method.See the matrix viewer example below.}\section{Implementation}{Reference class definitions need to store the definition of the fieldsand also the class-based methods (since these belong to the class in theclass-based paradigm).These and some other properties are defined in the class\code{refClassRepresentation}, which is a subclass of the ordinaryclass definition class, \linkS4class{classRepresentation}.As of version 2.12.0 of \R, the details of this class are not part ofthe API.Helper functions \code{refClassFields} and \code{refClassMethods} willextract class field and method information.It's a bad idea to count on the specific slots as currently documented(to satisfy package checking).The implementation provides for field and class method access throughthe \code{$} operator. %$This includes replacement of field values in a reference class object.Class method definitions are also supported as documented here.The implementation for both purposes works by delivering a functionobject for the method with an environment containing the fields andclass-based methods as objects, specificallythe environment of the object itself.The \code{initialize} method for standard reference class objectsinstalls an environment in the object with the initial contents of theclass fields (either the defaults from the prototype or valuessupplied explicitly in the call to \code{\link{new}}).Methods are inserted into the environment when they are called,including other methods they may call.These dependencies are searched heuristically when the reference classis defined, using the \code{findGlobals} function in recommendedpackage \code{codeTools}.This recommended package must be available to define a reference class(but is not required to use reference classes defined in an installed package).}\section{Inter-System Interfaces}{A number of \R packages currently support interfaces to otherlanguages using a similar reference-based programming model.Aside from differences in choice of terminology and other details,many of these are largely compatible with the programming styledescribed here.The inter-system interface approach could also be added to thereference class approach here, for example by having a basic referenceclass that was a subclass of \code{stdRefClass} but assembled methodsand field accessors that were proxies to the facilities in the otherlanguage. Such an implementation would allow users to write \Rsoftware that extended methods and fields in the other language withadditional ones implemented in \R. Users of such packages should seea consistent interface to both sets of fields and methods. As ofversion 2.12.0 of \R, this remains a topic for future development.}\value{\code{setRefClass} returns the class definition, invisibly, but the function is mainlycalled for the side effect of assigning the definition, usually aspart of an \R{} package.\code{refClassFields} returns a list of the classes for all the classfields.\code{refClassMethods} returns a list of the class method functions,including those inherited and accessor methods generated.}\author{John Chambers}\examples{## a simple editor for matrix objects. Method edit() changes some## range of values; method undo() undoes the last edit.setRefClass("matrixEditor",fieldClasses = list( data = "matrix",edits = "list"),classMethods = list(edit = function(i, j, value) {x <- getData()backup <-list(i, j, x[i,j])setEdits(c(list(backup),getEdits()))x[i,j] <- valuesetData(x)invisible(value)},undo = function() {prev <- getEdits()if(length(prev)) prev <- prev[[1]]else stop("No more edits to undo")edit(prev[[1]], prev[[2]], prev[[3]])## trim the edits listsetEdits(getEdits()[-(1:2)])invisible(prev)},save = function(file) {base::save(.self, file = file)}))xMat <- matrix(1:12,4,3)xx <- new("matrixEditor", data = xMat)xx$edit(2, 2, 0)xx$dataxx$undo()stopifnot(all.equal(xx$data, xMat))\dontshow{tf <- tempfile()xx$save(tf) #$load(tf)unlink(tf)stopifnot(identical(xx$data, .self$data))}## A version of the classMethods= argument above that## is a little simpler and faster but only works with## the implementation in R, not with interfaces:localMethods <- list(edit = function(i, j, value) {backup <-list(i, j, data[i,j])edits <<- c(list(backup),edits)data[i,j] <<- valueinvisible(value)},undo = function() {prev <- getEdits()if(length(prev)) prev <- prev[[1]]else stop("No more edits to undo")edit(prev[[1]], prev[[2]], prev[[3]])## trim the edits listlength(edits) <<- length(edits) - 2invisible(prev)},save = function(file) {base::save(.self, file = file)})setRefClass("matrixEditor2",fieldClasses = list( data = "matrix",edits = "list"),classMethods = localMethods)## Inheriting a reference class: a matrix viewersetRefClass("matrixViewer", contains = "matrixEditor",classMethods = list( view = function() matplot(data),edit = # invoke previous method, then replotfunction(i, j, value) {callSuper(i, j, value) # but this doesn't yet workview()}))\dontshow{ff = new("matrixEditor2", data = xMat)## uncomment when callSuper() works## ff$edit(2,2,0)## ff$data## ff$undo()## stopifnot(all.equal(ff$data, xMat))removeClass("matrixEditor")removeClass("matrixEditor2")removeClass("matrixViewer")}}\keyword{ programming }\keyword{ classes }