[Rd] Reference classes

John Chambers jmc at r-project.org
Tue Oct 26 17:57:55 CEST 2010

What you've written will certainly generate an infinite recursion.  How 
could it not?

Specifying an accessor function says to the system "Any reference to 
this field should be evaluated by calling this function."  But then you 
refer to the field in the function itself, which will result in a call 
to the function, which ....

Accessor functions are typically used when the field is a proxy for some 
data stored in a less convenient form (in a C++ object in the case of 
Rcpp).  As a self-contained example:

 > ss <- setRefClass("silly", fields = list(now = function(value) {
+ if(missing(value)) Sys.time()
+ else stop("You can't change the time, dummy!")
+ }))
 > s1 <- ss$new()
 > s1$now
[1] "2010-10-26 08:50:36 PDT"
 > s1$now <- "Never"
Error in function (value)  : You can't change the time, dummy!

On 10/26/10 4:57 AM, Jon Clayden wrote:
> On 23 October 2010 00:52, Jon Clayden<jon.clayden at gmail.com>  wrote:
>> On 22 October 2010 18:55, John Chambers<jmc at r-project.org>  wrote:
>>>> As a suggestion, it would be nice if the accessors() method could be
>>>> used to create just "getters" or just "setters" for particular fields,
>>>> although I realise this can be worked around by removing the unwanted
>>>> methods afterwards.
>>> In other words, read-only fields.  There is a facility for that implemented
>>> already, but it didn't yet make it into the documentation, and it could use
>>> some more testing.  The generator object has a $lock() method that inserts a
>>> write-once type of method for one or more fields.  Example:
>>>> fg<- setRefClass("foo", list(bar = "numeric", flag = "character"),
>>> +             methods = list(
>>> +             addToBar = function(incr) {
>>> +                 b = bar + incr
>>> +                 bar<<- b
>>> +                 b
>>> +             }
>>> +             ))
>>>> fg$lock("bar")
>>>> ff = new("foo", bar = 1.5)
>>>> ff$bar<- 2
>>> Error in function (value)  : Field "bar" is read-only
>>> A revision will document this soon.
>>> (And no, the workaround is not to remove methods.  To customize access to a
>>> field, the technique is to write an accessor function for the field that, in
>>> this case, throws an error if it gets an argument.  See the documentation
>>> for the fields argument.  The convention here and the underlying mechanism
>>> are taken from active bindings for environments.)
>> OK, yes - I see. This is clearly much less superficial than removing
>> the setter method for a field which can be directly set anyway. I'll
>> have to try out field accessor functions and get a feel for the
>> semantics.
> Unfortunately, I'm having difficulty working out the accessor function
> approach. I've looked in the Rcpp package for examples, but it doesn't
> seem to use this feature. If I define
> Foo<- setRefClass("Foo", fields=list(bar=function (newBar) {
>                                              if (missing(newBar)) bar
>                                              else stop("bar is read-only") }),
>                            methods=list(barExists=function ()
> print(exists("bar"))))
> then I can't access the value of "bar" due to infinite recursion.
> Using ".self$bar" in the accessor produces the same effect.
>> f<- Foo$new()
>> f$barExists()
> [1] TRUE
>> f$bar
> Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
>> f$bar()
> Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
> I can guess why this is happening (accessing "bar" within the accessor
> calls itself), but how can I get at the value of "bar" within the
> accessor without this occurring?
> The other problem is that I can't even set a value at the time of
> creation of the object, viz.
>> f<- Foo$new(bar=2)
> Error in function (newBar)  : bar is read-only
> Is there a way to test whether "bar" has already been set in the
> accessor, so that I can allow it to be set once? (I know lock() allows
> this, but it would be useful to be able to replicate the effect using
> accessors, so that it can be generalised further where needed.)
> Clearly, exists("bar") doesn't do this, as seen above -- presumably
> because it sees the method rather than the field, or there is some
> default value.
> Thanks in advance,
> Jon

More information about the R-devel mailing list