Search code examples
rr6

R6Class : initialize method raises the error message: "cannot add bindings to a locked environment"


My working environment:

R version: 3.6.3 (64 bits)
OS: Windows 10 (64 bits)

I was working on the following exercice from Hadley Wickham's advanced R book:

Create a bank account R6 class that stores a balance and allows you to deposit and withdraw money.

Here is the class that I created

library(R6)
BankAccount <- R6Class(
    classname = "BankAccount",
    public = list(
        initialize = function(first_name,
                              last_name,
                              email,
                              balance
                              ) {
            stopifnot(balance >= 0)
            self$balance <- balance
            self$email <- email
            self$first_name <- first_name
            self$last_name <- last_name
        },
        deposit = function(amount) {
            stopifnot(amount > 0)
            self$balance <- self$balance + amount
            invisible(self)
        },
        withdraw = function(amount) {
            stopifnot(amount > 0, self$balance - amount > 0)
            self$balance = self$balance - amount
            invisible(self)
        },
        print = function() {
            cat(
                "first name: ",
                self$first_name,
                "\n",
                "last name: ",
                self$last_name,
                "\n",
                "email : ",
                self$email,
                "\n",
                "current balance : ",
                self$balance,
                "\n"
                )
            invisible(self)
        }
    )
)


my_bank_account <- BankAccount$new(
                                   first_name = "test_firstname",
                                   last_name = "test_lastname",
                                   email = "[email protected]",
                                   balance = 140
                               )

The above code raises the following error upon execution:

Error in self$balance <- balance (from #10) : 
  cannot add bindings to a locked environment
> 

I cannot understand the problem in the initialize method of my class. What's wrong with the initialize function in my code?


Solution

  • Well, I had to read more carefully the R6Class documentation:

    If the public or private lists contain any items that have reference semantics (for example, an environment), those items will be shared across all instances of the class. To avoid this, add an entry for that item with a 'NULL' initial value, and then in the 'initialize' method, instantiate the object and assign it.

    The problem with my code was that I had declared fields only inside my constructor but apparently they had to be declared first, out of the initializer function, assigned by NULL, and then inside the initializer be assigned by the corresponding function arguments. So here is the correct and new version of my class

    library(R6)
    BankAccount <- R6Class(
        classname = "BankAccount",
        public = list(
            ## I had forgotten to write the
            ## following 4 lines in the previous
            ## version and apparently this caused
            ## the problem.
            balance = NULL,
            email = NULL,
            first_name = NULL,
            last_name = NULL,
            ##
            ##
            initialize = function(first_name,
                                  last_name,
                                  email,
                                  balance
                                  ) {
                stopifnot(balance >= 0)
                self$balance <- balance
                self$email <- email
                self$first_name <- first_name
                self$last_name <- last_name
            },
            deposit = function(amount) {
                stopifnot(amount > 0)
                self$balance <- self$balance + amount
                invisible(self)
            },
            withdraw = function(amount) {
                stopifnot(amount > 0, self$balance - amount > 0)
                self$balance = self$balance - amount
                invisible(self)
            },
            print = function() {
                cat(
                    "first name: ",
                    self$first_name,
                    "\n",
                    "last name: ",
                    self$last_name,
                    "\n",
                    "email : ",
                    self$email,
                    "\n",
                    "current balance : ",
                    self$balance
                    )
                invisible(self)
            }
        )
    )    
    my_bank_account <- BankAccount$new(
                                           first_name = "test_firstname",
                                           last_name = "test_lastname",
                                           email = "[email protected]",
                                           balance = 140
                                       )
        print(my_bank_account)
    

    And this time the code runs without any problem.