r-lib / R6

Encapsulated object-oriented programming for R
https://R6.r-lib.org
Other
412 stars 57 forks source link

Can't override dynamically created method #277

Closed pchabros closed 1 year ago

pchabros commented 1 year ago

Reproducible example:

Parent <- R6::R6Class(
  "Parent",
  lock_objects = FALSE,
  public = list(
    initialize = function() {
      # I wan't to create many methods dynamically in the loop.
      self[["foo"]] <- function() {
        print('foo')
      }
    }
  )
)

Child <- R6::R6Class(
  "Child",
  inherit = Parent,
  lock_objects = FALSE,
  public = list(
    foo = function() {
      super$foo()
    }
  )
)

child <- Child$new()

Error:

Error in self$foo <- function() { : 
  cannot change value of locked binding for 'foo'

Why I'm getting this error when in both Parent and Child lock_objects = FALSE?

wch commented 1 year ago

The option lock_objects=FALSE makes it possible to add new members to an object, but it doesn't make it possible to alter "normal" members that are added at construction time (ones which are listed in public, private, or active).

At a low level, lock_objects=FALSE works by not locking the environment that the constitutes the R6 object. However, the members that were added at construction time are still locked bindings in the environment.

If you want to change a locked binding, you can use unlockBinding(), as in:

Parent <- R6::R6Class(
  "Parent",
  lock_objects = FALSE,
  public = list(
    initialize = function() {
      unlockBinding("foo", self)
      # I wan't to create many methods dynamically in the loop.
      self[["foo"]] <- function() {
        print('foo')
      }
    }
  )
)

Child <- R6::R6Class(
  "Child",
  inherit = Parent,
  lock_objects = FALSE,
  public = list(
    foo = function() {
      super$foo()
    }
  )
)

child <- Child$new()

However, if you do this, some things may not work as expected, like cloning.