Search code examples
rreference-class

Using "[[ ]]" notation for reference class methods


While experimenting with the new reference classes in R I noticed some odd behaviour if you use the "[[ ]]" notation for methods (X[["doSomething"]] instead of X$doSomething). This notation works for fields, but I initially thought it wouldn't work for methods until I found that if you execute "class(X$doSomething)" you can then use "[[ ]]" afterwards. The simple example below illustrates the point.

setRefClass("Number", 
  fields = list(
    value = "numeric"
  ),
  methods = list(
    addOne = function() {
      value <<- value + 1
    }
  )
)

X <- new("Number", value = 1)

X[['value']]         # 1

X[["addOne"]]()      # Error: attempt to apply non-function
class(X[["addOne"]]) # NULL

class(X$addOne)      # "refMethodDef"

# Now the following works!
X[["addOne"]]()      # sets X$value = 2
class(X[["addOne"]]) # "refMethodDef"

The reason I encountered this is because I want to group my objects together in a list and create an "applyMethod" function which applies a specified method on each of the objects within. Therefore, I need to specify the method as a string. Does anyone have any ideas how I can achieve this?


Solution

  • Here's a class

    .A <-
        setRefClass("A",
                    fields=list(x="numeric"),
                    methods=list(foo=function() x))
    

    If I had an instance a and wanted to construct a call to the 'foo' method using '$' I could

    eval(substitute(a$FUN(), list(FUN="foo")))
    

    So I'll create a class Alist that is meant to have a list of elements of class A (this could be enforced programmatically), and that has a .delegate method that'll apply an arbitrary method to all elements of the list. I'll then add a method that delegates foo.

    .delegate <- function(FUN, ...)
    {
        lapply(elts, function(elt, ...) {
            eval(substitute(elt$FUN(...), list(FUN=FUN, ...)))
        })
    }
    
    .Alist <-
        setRefClass("Alist",
                    fields=list(elts="list"),
                    methods=list(
                      initialize = function(...) callSuper(elts=list(...)),
                      .delegate = .delegate,
                      foo=function() .delegate("foo")))
    

    And then use it

    > aList <- .Alist$new(.A$new(x=1), .A$new(x=2))
    > aList$foo()
    [[1]]
    [1] 1
    
    [[2]]
    [1] 2