Skip to content
Development
Skill

/r-oop

R object-oriented programming guide for S7, S3, S4, and vctrs. Use when designing R classes or choosing an OOP system.

From plugin
r-skills
2138 skills3 agents4 commands4 hooks

Claude Code

$ npx -y skills add ab604/claude-code-r-skills --skill r-oop --agent claude-code

SKILL.md

r-oop.SKILL.md
name: r-oop
description: R object-oriented programming guide for S7, S3, S4, and vctrs. Use when designing R classes or choosing an OOP system.

R Object-Oriented Programming

*S7, S3, S4, and vctrs: choosing the right OOP system for your needs*

S7: Modern OOP for New Projects

  • **S7 combines S3 simplicity with S4 structure**
  • **Formal class definitions with automatic validation**
  • **Compatible with existing S3 code**
# S7 class definition
Range <- new_class("Range",
  properties = list(
    start = class_double,
    end = class_double
  ),
  validator = function(self) {
    if (self@end < self@start) {
      "@end must be >= @start"
    }
  }
)

# Usage - constructor and property access
x <- Range(start = 1, end = 10)
x@start  # 1
x@end <- 20  # automatic validation

# Methods
inside <- new_generic("inside", "x")
method(inside, Range) <- function(x, y) {
  y >= x@start & y <= x@end
}

OOP System Decision Matrix

S7 vs vctrs vs S3/S4 Decision Tree

**Start here:** What are you building?

1. Vector-like objects (things that behave like atomic vectors)

Use vctrs when:
- Need data frame integration (columns/rows)
- Want type-stable vector operations
- Building factor-like, date-like, or numeric-like classes
- Need consistent coercion/casting behavior
- Working with existing tidyverse infrastructure

Examples: custom date classes, units, categorical data

2. General objects (complex data structures, not vector-like)

Use S7 when:
- NEW projects that need formal classes
- Want property validation and safe property access (@)
- Need multiple dispatch (beyond S3's double dispatch)
- Converting from S3 and want better structure
- Building class hierarchies with inheritance
- Want better error messages and discoverability

Use S3 when:
- Simple classes with minimal structure needs
- Maximum compatibility and minimal dependencies
- Quick prototyping or internal classes
- Contributing to existing S3-based ecosystems
- Performance is absolutely critical (minimal overhead)

Use S4 when:
- Working in Bioconductor ecosystem
- Need complex multiple inheritance (S7 doesn't support this)
- Existing S4 codebase that works well

Detailed S7 vs S3 Comparison

| Feature | S3 | S7 | When S7 wins | |---------|----|----|---------------| | **Class definition** | Informal (convention) | Formal (`new_class()`) | Need guaranteed structure | | **Property access** | `$` or `attr()` (unsafe) | `@` (safe, validated) | Property validation matters | | **Validation** | Manual, inconsistent | Built-in validators | Data integrity important | | **Method discovery** | Hard to find methods | Clear method printing | Developer experience matters | | **Multiple dispatch** | Limited (base generics) | Full multiple dispatch | Complex method dispatch needed | | **Inheritance** | Informal, `NextMethod()` | Explicit `super()` | Predictable inheritance needed | | **Migration cost** | - | Low (1-2 hours) | Want better structure | | **Performance** | Fastest | ~Same as S3 | Performance difference negligible | | **Compatibility** | Full S3 | Full S3 + S7 | Need both old and new patterns |

Practical Guidelines

Choose S7 when you have

# Complex validation needs
Range <- new_class("Range",
  properties = list(start = class_double, end = class_double),
  validator = function(self) {
    if (self@end < self@start) "@end must be >= @start"
  }
)

# Multiple dispatch needs
method(generic, list(ClassA, ClassB)) <- function(x, y) ...

# Class hierarchies with clear inheritance
Child <- new_class("Child", parent = Parent)

Choose vctrs when you need

# Vector-like behavior in data frames
percent <- new_vctr(0.5, class = "percentage")
data.frame(x = 1:3, pct = percent(c(0.1, 0.2, 0.3)))  # works seamlessly

# Type-stable operations
vec_c(percent(0.1), percent(0.2))  # predictable behavior
vec_cast(0.5, percent())          # explicit, safe casting

Choose S3 when you have

# Simple classes without complex needs
new_simple <- function(x) structure(x, class = "simple")
print.simple <- function(x, ...) cat("Simple:", x)

# Maximum performance needs (rare)
# Existing S3 ecosystem contributions

S3 Patterns

Basic S3 Class

# Constructor
new_person <- function(name, age) {
  stopifnot(is.character(name), length(name) == 1)
  stopifnot(is.numeric(age), length(age) == 1)

  structure(
    list(name = name, age = age),
    class = "person"
  )
}

# Print method
print.person <- function(x, ...) {
  cat("Person:", x$name, "(age", x$age, ")\n")
  invisible(x)
}

# Generic + method
greet <- function(x) UseMethod("greet")
greet.person <- function(x) {
  cat("Hello, my name is", x$name, "\n")
}
greet.default <- function(x) {
  cat("Hello!\n")
}

S3 Inheritance

# Child class
new_employee <- function(name, age, company) {
  obj <- new_person(name, age)
  obj$company <- company
  class(obj) <- c("employee", class(obj))
  obj
}

# Method with inheritance
print.employee <- function(x, ...) {
  NextMethod()  # Call parent print method
  cat("Works at:", x$company, "\n")
  invisible(x)
}

S7 Patterns

Basic S7 Class

library(S7)

# Define class
Person <- new_class("Person",
  properties = list(
    name = class_character,
    age = class_numeric
  ),
  validator = function(self) {
    if (self@age < 0) {
      "@age must be non-negative"
    }
  }
)

# Create instance
bob <- Person(name = "Bob", age = 30)
bob@name  # "Bob"
bob@age <- 31  # Validated assignment

S7 Methods

# Define generic
greet <- new_generic("greet", "x")

# Add method
method(greet, Person) <- function(x) {
  cat("Hello, my name is", x@name, "\n")
}

# Default method
method(greet, class_any) <- function(x) {
  cat("Hello!\n")
}

S7 Inheritance

Employee <- new_class("Employee",
  parent = Person,
  properties = list(
    company = class_character
  )
)

# Override method
method(greet, Employee) <- function(x) {
  super(x, Person)@g
Read more
@ab604
Ships withr-skills

A curated collection of Claude Code configurations for modern R use. These skills, rules, commands, and agents help Claude Code understand R best practices and generate idiomatic, high-quality R code.

2138 skills3 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
213
Star history of ab604/claude-code-r-skills on GitHub, a chart by star-history.com

Other skills on r-skills.

r-bayes

r-bayes

Patterns for Bayesian inference in R using brms, including multilevel models, DAG validation, and marginal effects. Use when…

@ab604@ab604Install Skill
r-package-development

r-package-development

R package development guide covering dependencies, API design, testing, and documentation. Use when developing R packages.

@ab604@ab604Install Skill
r-performance

r-performance

R performance best practices including profiling, benchmarking, vctrs, and optimization strategies. Use when optimizing R code.

@ab604@ab604Install Skill
r-style-guide

r-style-guide

R style guide covering naming conventions, spacing, layout, and function design best practices. Use when writing R code.

@ab604@ab604Install Skill
rlang-patterns

rlang-patterns

rlang metaprogramming patterns for data-masking, injection operators, and dynamic dots. Use when writing functions that use tidy…

@ab604@ab604Install Skill
tdd-workflow

tdd-workflow

Test-driven development workflow for R using testthat. Use when writing new features, fixing bugs, or refactoring code. Enforces…

@ab604@ab604Install Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills2w309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react5d252k
@facebook@facebookInstall Skill

Questions about r-oop.

Which coding agents can use r-oop?
r-oop installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.