-
Notifications
You must be signed in to change notification settings - Fork 755
R has three object oriented (OO) systems: S3, S4 and R5. This page describes the new reference based class system, colloquially know as R5.
R5 is new in R 2.12. They fill a long standing need for mutable objects that had previously been filled by non-core packages like R.oo
, proto
and mutatr
. While the core functionality of R5 is solid, reference classes are still under active development and some details will change.
There are two main differences between R5 and S3 and S4:
- R5 objects use message-passing OO
- R5 objects are mutable: the usual R copy on modify semantics do not apply
These properties makes this object system behave much more like Java and C#. Surprisingly, the implementation of R5 is almost entirely in R code - they are a combination of S4 methods and environments. This is a testament to the flexibility of S4.
Particularly suited for: simulations where you're modelling complex state, GUIs.
Note that when using reference based classes we want to minimise side effects, and use them only where mutable state is absolutely required. The majority of functions should still "functional", and side effect free. This makes code easier to reason about (because you don't need to worry about methods changing things in surprising ways), and easier for other R programmers to understand.
Limitations: can't use enclosing environment - because that's used for the object.
Creating a new reference based class is straightforward: you use setRefClass
. You can create objects using the new
function as with S4
, or with the new method of the class object:
setRefClass("Person")
new("Person")
# Or keep reference to class around.
Person <- setRefClass("Person")
Person$new()
A reference class has three main components:
-
fields
, the equivalent of slots inS4
-
methods
, functions which operate within the context of the object and can modify it -
contains
, the classes which the class inherits from. It is possible to inherit from non-reference classes, but this is not something I'd encourage as the potential for confusion is high
These are the three most important arguments to setRefClass
and are illustrated below:
setRefClass("Animal")
# Specify parent classes
setRefClass("Pet", contains = "Animal", fields = "name")
# Fields can be a named list of object classes
setRefClass("Pet", contains = "Animal", fields =
list(name = "character", age = "numeric"))
new("Pet", name = "Mina", age = 3)
new("Pet", age = "three")
# Or a list of "accessor" functions
setRefClass("Pet", contains = "Animal", fields = list(
"_name" = "character",
name = function(value) {
if (!missing(value)) {
message("Setting name")
`_name` <<- value
} else {
message("Getting name")
`_name`
}
}))
Mina <- new("Pet")
Mina$name <- "Mina"
Mina$name
Keeping a reference to the class around is also useful because you can later modify it:
# Instead of creating a class all at once:
setRefClass("Person", methods = list(
say_hello = function() message("Hi!")
))
# You can build it up piece-by-piece
Person <- setRefClass("Person")
Person$methds(say_hello = function() message("Hi!"))
It's not currently possible to modify fields because adding fields would invalidate existing objects that didn't have those fields.
The object returned by setRefClass
(or retrieved later by getRefClass
) is called a generator object. It has methods:
-
new
: like a factory method methods
help
fields
lock
accessors
R5 methods are associated with objects, not with functions, and are called using the special syntax obj$method(arg1, arg2, ...)
. (You might recall we've seen this construction before when we called functions stored in a named list). Methods are also special because they can modify fields. This is different
We've also seen this construct before, when we used closures to create mutable state. Reference classes are just some extra syntax on top of that.
Modify fields with <<-
. Will call accessor functions if defined.
Special fields: .self
(Don't use fields with names starting with .
as these may be used for special purposes in future versions.)
obj$callSuper
obj$copy
obj$field
obj$getRefClass
-
obj$import
andobj$export
obj$initFields
Python style doc-strings. obj$help()
.
Note: collation Note: namespaces and exporting