Search code examples
rr-s3

What are these square brackets for S3 classes?


I obtained this from an open source repo on git. This shows the writing of generic and methods for S3 classes. But I do not understand the notations or conventions that the functions are being assigned to. The following are my questions:

  • The use backticks `` to define the function name. Usually we wouldn't use backticks or even double quotes to assign variables/functions but I see this happening a lot of times. Is this a naming convention?
  • Why is the . included before the blob name? Usually wouldn't it just be called blob and a method would be method.blob?
  • Why are there [ brackets there? Especially, [<- and [[<-. Are we performing some sort of double asigning?

Hopefully someone will be able to shed some light on what is ha

#' @export
 `[.blob`  <- function(x, i, ...) {
  new_blob(NextMethod())
}

#' @export
`[<-.blob` <- function(x, i, ..., value) {
  if (!is_raw_list(value)) {
    stop("RHS must be list of raw vectors", call. = FALSE)
  }

  NextMethod()
}

#' @export
 `[[<-.blob` <- function(x, i, ..., value) {
  if (!is.raw(value) && !is.null(value)) {
    stop("RHS must be raw vector or NULL", call. = FALSE)
  }

  if (is.null(value)) {
    x[i] <- list(NULL)
    x
  } else {
    NextMethod()
  }
}

Solution

  • Summary

    If you're creating a new object in R for which you want 'different' subset and assignment behaviour, you should create the associated methods for these operations.

    • The . IS working in the way you're expecting - method dispatch

    • [.blob is overriding the S3 [ subset operator

    • [<-.blob is overriding the S3 [<- operator (i.e. vector-subset assignment)

    • [[<-.blob is overriding the S3 [[<- operator (i.e. list-subset assignment)

    • Special symbols (e.g., backticks, brackets, percent-sign, variables with spaces in the name) cannot be "assigned to" by default. To do so, if you surround it in backticks, it can work. As an example, a variable named A B cannot be assigned with A B <- 1, whereas `A B` <- 1 works (credit @r2evans)

    Examples

    subset

    Taking [.blob as an example, this allows you to create your own subset operation for your blob object.

    ## Create your own blob object (class)
    blob <- 1:5
    attr(blob, "class") <- "blob"
    
    ## create a subset operator, which in this example just calls the next method in the s3 dispatch chain 
    `[.blob` <- function(x, i, j, ...) NextMethod()
    

    As we're not doing anything special in our own subset method, this works like normal R vectors

    blob[3]
    # [1] 3
    

    However, we can make the subset operation do whatever we want, for example always return the 1st element of the vector

    ## define the function to always subset the first element
    `[.blob` <- function(x, i, j, ...) { i = 1; NextMethod() }
    

    Now your blob object will only ever return the 1st element.

    blob[1]
    # [1] 1
    blob[2]
    # [1] 1
    blob[3]
    # [1] 1
    

    Assignment

    Similarly for one of the assignment operators, if you overload [<- with

    `[<-.blob` <- function(x, i, j, ...) { i = 5; NextMethod() }
    

    This will always assign the 5th element of your blob object with the new value

    blob[1] <- 100
    blob
    # [1]   1   2   3   4 100
    # attr(,"class")
    # [1] "blob"
    

    Back ticks

    The back-ticks are used so we can assign functions/variables to special symbols.

    For example, try to assign a vector to the [ symbol

    [ <- 1:5
    # Error: unexpected '[' in "["
    

    Whereas surrounding it with ticks lets it pass (although this example is not recommended)

    `[` <- 1:5
    `[`
    # [1] 1 2 3 4 5