2

I'm currently exploring the possibilities of R's Reference Class and I'm trying to wrap my head around customized accessor functions. The manual states for field that:

The element in the list can alternatively be an accessor function, a function of one argument that returns the field if called with no argument or sets it to the value of the argument otherwise. Accessor functions are used internally and for inter-system interface applications. Their definition follows the rules for writing methods for the class: they can refer to other fields and can call other methods for this class or its superclasses. See the section on “Implementation” for the internal mechanism used by accessor functions.

All I've been able to find is using accessor functions in the context of file storage. Being accustomed to private internal variables and input validations I would argue that this is where the input data validation should be, see example below:

Account <- 
  setRefClass("Account",
              fields = list(data = "list",
                            balance = 
                              function(value){
                                if (missing(value)){
                                  return(data$balance)
                                }else{
                                  if (!is.numeric(value))
                                    stop("You can only set the balance to a numeric value!")
                                  data$balance <<- value
                                }
                              }),
              methods = list(
                withdraw = function(x) {
                  balance <<- balance - x
                },
                deposit = function(x) {
                  balance <<- balance + x
                }
              ))

This works as expected:

> a <- Account$new(balance = 0)
> 
> a$deposit(10)
> a$balance
[1] 10
> 
> a$withdraw(1)
> a$balance
[1] 9
> 
> a$balance <- "a"
 Error in (function (value)  : 
  You can only set the balance to a numeric value! 

What I would like to know if there is a reason for not doing this since it seems like a natural approach but not mentioned in the manual? Is there a good way of completely hiding the data variable, e.g. using .self <- local({data = list(); .self}) at some point.

Max Gordon
  • 5,367
  • 2
  • 44
  • 70

1 Answers1

0

I've been struggling with this as well and it appears that there is no way to fully hide the data. One thing that I noticed with your example is that you can still manually change the balance value by calling:

a <- Account$new(balance = 0)   
a$data$balance<-"a"
a$balance
#> [1] "a"

The reason it can still be manipulated is the reason I suspect it is not recommended. When the manual describes accessor functions it seems to be referring to the ones that you get if you want to use $accessor (described in the manual). Example below:

Account <- 
  setRefClass("Account",
              fields = list(balance = "numeric"),
              methods = list(
                withdraw = function(x) {
                  balance <<- balance - x
                },
                deposit = function(x) {
                  balance <<- balance + x
                }
              ))
Account$accessors("balance")
a<-Account$new("balance"=0)
# you can now get and set the balance with getBalance() and setBalance(x).
# (it automatically capitalizes your field name) 
a$setBalance(10)
a$getBalance()
# [10]

Lastly, you can always create and set the getBalance()/setBalance(x) methods manually if you want to add extra checks in the methods argument for setClassRef. Most of this is inferred from the documentation for setClassRef. See this link on stackoverflow below which discusses Private Members.

Community
  • 1
  • 1
vitallish
  • 312
  • 1
  • 12
  • I've seen the post, I believe there is a way that within the limits of R makes variables behave in a private manner. @hadley suggests in his excellent book, Advanced R, in an [exercise](http://adv-r.had.co.nz/OO-essentials.html): *Use a field function to prevent the account balance from being directly manipulated*. He suggests using a `.balance` field and a balance field function - but it does not really hide the field. I've tried using `balance = local({balance <- NULL; my_func()...})` but that is equivalent to a [static variable](http://en.wikipedia.org/wiki/Static_variable). – Max Gordon Jan 25 '15 at 13:27