Skip to content
AgileLogics
6 min read

Object-Oriented R: S3

Prerequisites: Lists, Functions

R has several object systems. The oldest and most widely used is S3. You use it constantly without noticing: print(), summary(), and plot() behave differently for data frames, models, and factors because of S3.

How S3 works

An S3 object is just an ordinary R object (usually a list) with a class attribute. A generic function like print looks at the class and calls the matching method, named generic.class:

new_money <- function(amount, currency = "USD") {
  stopifnot(is.numeric(amount), is.character(currency))
  structure(list(amount = amount, currency = currency), class = "money")
}

print.money <- function(x, ...) {
  cat(sprintf("%.2f %s\n", x$amount, x$currency))
  invisible(x)
}

format.money <- function(x, ...) sprintf("%.2f %s", x$amount, x$currency)

price <- new_money(19.9, "EUR")
price
class(price)
paste("Total:", format(price))
19.90 EUR
[1] "money"
[1] "Total: 19.90 EUR"
  • structure(..., class = "money") creates the object.
  • print.money is the print method for class money, so typing price in the console uses it automatically.
  • A constructor function (new_money) that validates inputs keeps objects consistent.

Writing your own generic

area <- function(shape, ...) UseMethod("area")

area.circle <- function(shape, ...) pi * shape$r^2
area.rectangle <- function(shape, ...) shape$w * shape$h
area.default <- function(shape, ...) stop("Don't know the area of a ", class(shape)[1])

c1 <- structure(list(r = 1), class = "circle")
r1 <- structure(list(w = 2, h = 3), class = "rectangle")

round(area(c1), 2)
area(r1)
[1] 3.14
[1] 6

UseMethod("area") dispatches to the method for the object's class, or to area.default.

Inheritance

The class attribute can be a vector; R tries each class in order:

x <- structure(list(), class = c("premium_account", "account"))
inherits(x, "account")
[1] TRUE

methods(print) lists all print methods; methods(class = "lm") lists everything you can do with a linear model.

When to use S3

S3 is simple and informal — ideal for giving your own data structures friendly print, summary, and format methods, which is exactly what most packages do. The next lesson covers the stricter alternatives.