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?
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.