2

It seems that the copy method does not work if you have an active binding.

Example class:

test <- setRefClass("test", fields = list(x =function() y + 1 , y = "numeric"))

Initializing, it works ok:

a <- test(y = 1)
a$x
[1] 2
a$y
[1] 1

Error on copy:

a$copy()
Error in (function ()  : unused argument (quote(2))

Is this the expected behavior?

R.version
platform       x86_64-w64-mingw32          
arch           x86_64                      
os             mingw32                     
system         x86_64, mingw32             
status                                     
major          3                           
minor          1.2                         
year           2014                        
month          10                          
day            31                          
svn rev        66913                       
language       R                           
version.string R version 3.1.2 (2014-10-31)
nickname       Pumpkin Helmet   
Carlos Cinelli
  • 11,354
  • 9
  • 43
  • 66
  • 1
    Active bindings need to have form `function(value) {}` to allow for assignment; `copy()` is trying to set the field to it's old 'value'. – Martin Morgan Jan 23 '16 at 02:01
  • Thanks a lot @MartinMorgan! This behavior still seems a little strange though, because active bindings with no arguments are quite common! Based on your answer I created a copy method that does not try to assign values to active bindings, please feel free to correct my answer if necessary! – Carlos Cinelli Jan 24 '16 at 10:35

1 Answers1

1

Based on Martin's comment, the problem lies on the assign() part of the default copy method:

 for (field in names(def@fieldClasses)) {
        if (shallow) 
            assign(field, get(field, envir = selfEnv), envir = vEnv)
        else {
            current <- get(field, envir = selfEnv)
            if (is(current, "envRefClass")) 
                current <- current$copy(FALSE)
            assign(field, current, envir = vEnv)
        }
    }

Since the field is an active binding with no arguments, the assignment will fail. One easy solution seems to be providing arguments to all active bindings. In my case I used the three dots.

test <- setRefClass("test", fields = list(x = function(...) y + 1 , y = "numeric"))

Now the copy method works fine. But the function does not throw an error if you try to assign values to active bindings (sometimes that is something I need in the use cases I have).

So another solution I tried was to redefine the copy method doing the for loop only for those fields that are not active bindings:

test <- setRefClass("test", fields = list( x = function() y + 1, y = "numeric"))

test$methods(copy = function (shallow = FALSE) 
{
  def <- .refClassDef
  value <- new(def)
  vEnv <- as.environment(value)
  selfEnv <- as.environment(.self)
  fields <- names(def@fieldClasses)[def@fieldClasses!=  "activeBindingFunction"]
  for (field in fields) {
    if (shallow) 
      assign(field, get(field, envir = selfEnv), envir = vEnv)
    else {
      current <- get(field, envir = selfEnv)
      if (is(current, "envRefClass")) 
        current <- current$copy(FALSE)
      assign(field, current, envir = vEnv)
    }
  }
  value
}
)

It seems to work ok in the cases I have tested. I have not tested this thoroughly though, so I don't know if it would break in other cases.

Carlos Cinelli
  • 11,354
  • 9
  • 43
  • 66