Avoiding consideration of enclosing frames when getting the value of a field of a reference class S4

I am a big fan of S4 Reference Classes, as they allow a hybrid programming style (functional / pass-by-value versus oop / pass-by-reference, example ) and thus greatly increase flexibility.

However, I think I just came across undesirable behavior regarding the method of scanning R through environments / frames when you ask it to get a specific field value using the $field() method (see the man page ). The problem is that R also seems to look in environments / frames if the desired field is not found in the real local / target environment (which would be the environment making up the S4 reference class), i.e. it just ran get(<objname>, inherits=TRUE) (see the man page ).

Actual question

To make R just look in the local / target environment, I thought of something like $field(name="<fieldname>", inherits=FALSE) , but $field() has no argument ... that would let me go through inherits=FALSE to get() (which I assume is called somewhere along the way). Is there any way around this?


Code example

For those who are interested in more detailed information: here is a small sample code illustrating the behavior

 setRefClass("A", fields=list(a="character")) x <- getRefClass("A")$new(a="a") 

Class A has field A , so it is in the target environment and the value is returned:

 > x$field("a") [1] "a" 

Everything looks different if we try to access a field that is not a field of the reference class, but has a name identical to the name of some other object in the workspace / search path (in this case "lm" ):

 require("MASS") > x$field("lm") function (formula, data, subset, weights, na.action, method = "qr", model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE, contrasts = NULL, offset, ...) { ret.x <- x ret.y <- y [omitted] if (!qr) z$qr <- NULL z } <bytecode: 0x02e6b654> <environment: namespace:stats> 

In fact, I did not expect this. IMHO a mistake or at least a warning would be much better. Or the opening method of $field() for arguments that can be passed along with other functions via ... I assume that somewhere along the path get() is called when $field() called, so something like this might interfere with the behavior described above:

 x$field("digest", inherits=FALSE) 

Workaround: Own Proposal

This should do the trick, but maybe there is something more elegant that is not related to the specification of the new method on top of $field() :

 setRefClass("A", fields=list(a="character"), methods=list( myField=function(name, ...) { # VALIDATE NAME // if (!name %in% names(getRefClass(class(.self))$fields())) { stop(paste0("Invalid field name: '", name, "'")) } # // .self$field(name=name) } ) ) x <- getRefClass("A")$new(a="a") > x$myField("a") [1] "a" > x$myField("lm") Error in x$myField("lm") : Invalid field name: 'lm' 
+8
pass-by-reference oop r s4 reference-class
source share
1 answer

By default, the field() method can be replaced with your own. So adding the inherits argument to avoid including frames is just a matter of capturing the existing x$field definition and adding it ...

 setRefClass( Class="B", fields= list( a="character" ), methods= list( field = function(name, value, inherits=TRUE ) { if( missing(value) ) { get( name, envir=.self, inherits=inherits ) } else { if( is.na( match( name, names( .refClassDef@fieldClasses ) ) ) ) { stop(gettextf("%s is not a field in this class", sQuote(name)), domain = NA) } assign(name, value, envir = .self) } } ), ) 

Or you may have a nice error message with a little redistribution

 setRefClass( Class="C", fields= list( a="character" ), methods= list( field = function(name, value, inherits=TRUE ) { if( is.na( match( name, names( .refClassDef@fieldClasses ) ) ) && ( !missing(value) || inherits==FALSE) ) { stop(gettextf("%s is not a field in this class", sQuote(name)), domain = NA) } if( missing(value) ) { get( name, envir=.self, inherits=inherits ) } else { assign(name, value, envir = .self) } } ), ) 

Since you can define any of your own methods to replace the default values, any logic you want can be implemented for your reflexes. Perhaps the error is if the variable is obtained using inheritance, but the mode matches c ("expression", "name", "character", "function") and a warning if it does not match the local field names of refClass?

+1
source share

All Articles