Instantiation of reference classes within reference classes - problems with lock() and immutability

300 Views Asked by At

I have come across some behaviour from R reference classes I would like to work around. In the following code, reference class B has two fields of reference class A in it.

These fields in B appear to be instantiated (possibly twice) with a zero-argument (default) versions of reference class A before B's initialize() method is called. These instances are then replaced with the correct versions of instance A during B's initialization process. The problem is that if I use lock() from B's instance generator, the initial empty instantiation's of A cannot be replaced in B. Another problem is that reference class A needs a default value in initialize [or a missing(c) test].

Help - suggestions - etc. appreciated.

A <- setRefClass('A',
    fields = list(
        count = 'numeric'
    ),

    methods = list(
        initialize = function (c=0) {
            cat('DEBUG: A$initialize(c); where c=');  cat(c); cat('\n')
            count <<- c
        }
    )
)

instance.of.A <- A$new(10)
str(instance.of.A)

B <- setRefClass('B',
    field = list(
        a = 'A',
        b = 'A'
    ),

    methods = list(
        initialize = function(c) {
            a <<- instance.of.A
            b <<- getRefClass('A')$new(c)
        }
    )
)

instance.of.b <- B$new(100)
str(instance.of.b)
2

There are 2 best solutions below

0
On BEST ANSWER

Drawing on the above, the solution I am using is (a little long because of the type checking):

A <- setRefClass('A',
    fields = list(
        count = function(x) {
            if (!missing(x)) {
                if(class(x) != 'numeric')
                    stop('Class A: count set by non-number')
                .self$count.private <- x
            }
            .self$count.private
        }
    ),

    methods = list(
        initialize = function (c=0) {
            cat('DEBUG: A$initialize(c); where c=');  cat(c); cat('\n')
            count <<- c
        }
    )
)

instance.of.A <- A$new(10)
str(instance.of.A)

B <- setRefClass('B',
    field = list(
        a = function(x) {
            if (!missing(x)) {
                if(!inherits(x, 'envRefClass') || class(x)[1] != 'A')
                    stop('Class B: expecting instance of class A')
                .self$a.private <- x
            }
            .self$a.private
        },
        b = function(x) {
            if (!missing(x)) {
                if(!inherits(x, 'envRefClass') || class(x)[1] != 'A')
                    stop('Class B: expecting instance of class A')
                .self$b.private <- x
            }
            .self$b.private
        }
    ),

    methods = list(
        initialize = function(c) {
            a <<- instance.of.A
            b <<- getRefClass('A')$new(c)
        }
    )
)

instance.of.b <- B$new(100)
str(instance.of.b)
1
On

Here are two possible solutions:

  1. Don't set fields attribute:

    B <- setRefClass('B',
        methods = list(
            initialize = function(c) {
              .self$a = instance.of.A
              .self$b =getRefClass('A')$new(c)
            }
         )
     )
    
  2. Set fields, but use the ANY class:

    B <- setRefClass('B',
        field = (a="ANY", b="ANY"), 
        methods = list(
            initialize = function(c) {
              a <<- instance.of.A
              b <<- getRefClass('A')$new(c)
            }
         )
     )
    

The downside of both these solutions is the type isn't enforced in a and b, i.e.

 B$a = "Fred"

is now possible.