Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

define a bracket (`[`) operator on an R6 class

Tags:

r

r6

Here's what does not work:

library(R6)

Foo = R6::R6Class(
    'Foo',
    public = list(
        X = NULL,
        metadata = NULL,
        initialize = function(X, metadata){
            self$X = X
            self$metadata = metadata
        },
        `[` = function(selection){
            subfoo = Foo$new(X = X[selection], 
                             metadata = self$metadata)
            return(subfoo)
        }
    )
)

Specifically, the [ method is garbage:

> X = matrix(1:8, ncol = 2)
> foo = Foo$new(X, 'blah blah')
> foo[1:2,]
Error in foo[1:2, ] : object of type 'environment' is not subsettable

The desired result is that foo[1:2,] would be an object of class Foo just like foo except that its matrix foo$X is smaller. Is there a direct way to implement this that exposes the [ operator directly to the user?

like image 654
zkurtz Avatar asked Jun 13 '18 16:06

zkurtz


2 Answers

An answer is better late than never I suppose. The problem is that you are registering methods that can be called like

x$`[`(1:3)

whereas you want

x[1:3]

The following will dispatch all [ and [<- calls (via S3) correctly for all R6 objects.

`[.R6` <- function(x, ...) x$`[`(...) 
`[<-.R6` <- function(x, ...) x$`[<-`(...) 

Note that you shouldn't do anything like this for the [[ methods, as these are already defined and used because R6 objects are environments.

Ideally it would be great if ( could also be overridden so that we could create functor objects (e.g. calling x(2)), but I don't know how this could be done..

like image 122
Ian Fellows Avatar answered Oct 20 '22 12:10

Ian Fellows


In case others are also looking for it, here is a complete working example of how to do this (based on Ian Fellows' answer):

library(R6)

Foo = R6::R6Class(
  'Foo',
  public = list(
    x = NULL,
    initialize = function(x) {
      self$x = x
    },
    
    `[` = function(idx) {
      self$x[idx]
    },
    
    `[<-` = function(idx, value) {
      self$x[idx] <- value
      invisible(self)  # important!
    },
    
    length = function() {
      length(self$x)
    }
  )
)

# set up method dispatch
`[.Foo`    <- function(obj, ...) obj$`[`(...) 
`[<-.Foo`  <- function(obj, ...) obj$`[<-`(...) 
length.Foo <- function(obj, ...) obj$length(...)

# test
foo <- Foo$new(c(1,2,3))
(n <- length(foo))
#> [1] 3
foo[1:n]
#> [1] 1 2 3
foo[2] <- 0
foo[1:n]
#> [1] 1 0 3

Created on 2020-10-09 by the reprex package (v0.3.0)

(The return value can be another Foo object, if you need it to be. I'm returning a vector for simplicity's sake)

like image 39
Tobias Hotzenplotz Avatar answered Oct 20 '22 10:10

Tobias Hotzenplotz