Using "[[ ]]" notation for reference class methods
Asked Answered
A

3

7

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?

Atomic answered 30/4, 2011 at 11:5 Comment(1)
Reference classes are supposed to be instantiated as .A = setRefClass(<...>); .A$new(). Is [[ meant to be used for member access? From ?ReferenceClasses the API is $. Might as well color within the lines.Nitaniter
N
4

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
Nitaniter answered 30/4, 2011 at 18:3 Comment(0)
S
2

basically R5 ref class does not cache the method until it is necessary. This is probably a kind of delayed evaluation.

And the caching takes place when you access the method via $. So, AFAIK, there is no way to access the method via [[string]]

But you can find a workaround using .dollarForEnvRefClass like this:

> X <- new("Number", value = 1)
> ls([email protected])
[1] "value" # no methods named "addOne" before caching
> X[["addOne"]]
NULL
> methods:::.dollarForEnvRefClass(X, "addOne") # cache it
Class method definition for method addOne()
function () 
{
    value <<- value + 1
}
<environment: 0x116a4aa00>
> ls([email protected])
[1] "addOne" "value"  # you can find it
> X$value # value is 1
[1] 1
> X[["addOne"]]() # call the method
> X$value  # the method works
[1] 2

if you are interested in more detail, see the implementation:
http://svn.r-project.org/R/trunk/src/library/methods/R/refClass.R

Maybe there is more straightforward way.

Stegosaur answered 30/4, 2011 at 12:22 Comment(1)
I believe the "R5" term is not the proper way to refer to Reference Classes. See the comments here #5137699Platoon
H
0

Report as bug on r-devel so John Chambers can fix it.

Hornwort answered 30/4, 2011 at 13:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.