Skip to content
hadley edited this page May 10, 2011 · 12 revisions

The R5 object system

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.

Classes and instances

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 in S4

  • 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

Methods

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.)

Common methods

  • obj$callSuper
  • obj$copy
  • obj$field
  • obj$getRefClass
  • obj$import and obj$export
  • obj$initFields

Documentation

Python style doc-strings. obj$help().

In packages

Note: collation Note: namespaces and exporting

Clone this wiki locally