Juggling with S7

Ella Kaye

July 9, 2026

siteswap

hex for the jugglr package, with the brightly-coloured letters of jugglr appearing to be thrown in a cascade pattern

ellakaye.github.io/jugglr

Create and validate (juggleable) siteswap

library(jugglr)
ss423 <- siteswap("423")
ss423
✔ '423' is valid vanilla siteswap
ℹ It uses 3 props
ℹ It is symmetrical with period 3

Create and validate (unjuggleable) siteswap

ss432 <- siteswap("432")
ss432
✖ '432' is not a valid juggling pattern
ℹ Two or more throws land on the same beat (collision)

Visualise with timeline

timeline(ss423)
Timeline diagram of the 423 siteswap pattern, showing the throws and catches of three props over time. Each throw is shown as an arc, with the height of the arc corresponding to the throw value. The arcs are coloured according to which prop is being thrown.

Visualise with timeline

timeline(ss432)
Timeline diagram of the 432 siteswap pattern, showing collisions and props appearing over time.

Visualise with timeline

ss44s <- siteswap("(4,4)")
timeline(ss44s, n_cycles = 6)
Timeline diagram of the (4,4) siteswap pattern, showing the throws and catches of four props over time, with props from one hand shown as arcs above a horizontal line and props from the other hand shown as arcs below the line. The arcs are coloured according to which prop is being thrown.

S7

object

class

generic

method

Class hierarchy

Siteswap  
├── vanillaSiteswap
└── synchronousSiteswap


ss423 <- siteswap("423")
class(ss423)
[1] "jugglr::vanillaSiteswap" "jugglr::Siteswap"       
[3] "S7_object"              

Helper to assign correct class

siteswap <- function(sequence) {
  if (is_vanilla_notation(sequence)) {
    vanillaSiteswap(sequence)
  } else if (is_sync_notation(sequence)) {
    synchronousSiteswap(sequence)
  } else {
    cli::cli_abort(
      "Not valid vanilla or synchronous siteswap notation."
    )
  }
}

Siteswap class constructor

library(S7)
Siteswap <- new_class(
  "Siteswap",
  properties = list(
    sequence = class_character
  )
)

Siteswap with property validator

Siteswap <- new_class(
  "Siteswap",
  properties = list(
    sequence = new_property(
      class = class_character,
      validator = function(value) {
        if (length(value) != 1) {
          "must be length 1"
        }
      }
    )
  )
)

Validation in practice

Siteswap("423")
<Siteswap>
 @ sequence: chr "423"
Siteswap(423)
Error:
! <Siteswap> object properties are invalid:
- @sequence must be <character>, not <double>
Siteswap(c("423", "3"))
Error:
! <Siteswap> object properties are invalid:
- @sequence must be length 1

vanillaSiteswap with computed properties

vanillaSiteswap <- new_class(
  "vanillaSiteswap",
  parent = Siteswap,
  properties = list(
    throws = new_property(
      class = class_integer,
      getter = function(self) {
        get_throws(self@sequence) # e.g. "423" -> c(4, 2, 3)
      }
    ),
    n_props = new_property(
      class = class_numeric,
      getter = function(self) {
        mean(self@throws)
      }
    ),
    valid = new_property(
      class = class_logical,
      getter = function(self) {
        no_collisions(self@throws) && is_whole_number(self@n_props)
      }
    )
  )
)

vanillaSiteswap

ss423 <- vanillaSiteswap("423")
ss423
<vanillaSiteswap>
 @ sequence: chr "423"
 @ throws  : num [1:3] 4 2 3
 @ n_props : num 3
 @ valid   : logi TRUE
ss423@n_props <- 4
Error:
! Can't set read-only property <vanillaSiteswap>@n_props

Defining methods

method(print, Siteswap) <- function(x, ...) {
  if (x@valid) {
    cli::cli_bullets(c(
      "v" = "'{x@sequence}' is valid siteswap",
      "i" = "It uses {x@n_props} props"
    ))
  } else {
    cli::cli_bullets(c(
      "x" = "'{x@sequence}' is not a valid juggling pattern",
    ))
  }
}
ss423
✔ '423' is valid siteswap
ℹ It uses 3 props

New generic and methods

timeline <- new_generic("timeline", "siteswap")
method(timeline, vanillaSiteswap) <- function(siteswap) {
  # ggplot2 code for vanillaSiteswap timeline
}
method(timeline, synchronousSiteswap) <- function(siteswap) {
  # ggplot2 code for synchronousSiteswap timeline
}

More in jugglr…

  • More properties, e.g. symmetry, period
  • More siteswap types: multiplex, synchronous multiplex, passing
  • More visualisations: ladder diagrams, throw data
  • More arguments to visualisation generics: e.g. n_cycles, title
  • Animations via Juggling Lab GIF server: viewer, file

S7 in packages

Package name in all classes

Siteswap <- new_class(
  "Siteswap",
  properties = list(
    ...
  )
  package = "jugglr"
)

What to document and export

roxygen2 8.0.0 provides first-class support for S7.

✓ Generics

✗ Methods

✓ Classes

@param, @prop

Registering methods: don’t @export!

in any .R file (in S7 v0.2.2):

.onLoad <- function(...) {
  methods_register()
}

Order matters

  • Subclass after parent class
  • Methods after generic and class
# vanillaSiteswap.R

#' @include siteswap.R # contains `Siteswap`
#' @include generics.R # contains `timeline`
NULL

Key takeaway

What are your questions?


ellakaye.github.io/jugglr

ellakaye.co.uk

Extra slides

Siteswap variations

  • vanilla: 423
  • synchronous: (4,4)(4x,4x)
  • multiplex: [54]24
  • synchronous multiplex: (2,6x)([6x4x],2x)
  • passing: <3p|3p> or <4.5 3 3 | 3 4 3.5>