Is there a way to declare public and private methods for S4 Reference Classes?
encapsulation, oop, private-methods, public-method, r
Solution
As functions are first-class objects in R, you can embed one inside the other, as follows:
hello <- function() {
print_ <- function() {
return ('hello world')
}
print_()
}
Yes, it's cheeky, probably not the cleanest way, but it does work... Invoke using 'hello()'.
Problem
Up-front: I am aware that R is a functional language, so please don't bite ;-) I've had great experiences with using an OOP approach for a lot of my programs. Now, I'm wondering if there's a way to make a distinction between public and private methods when using S4 Reference Classes in R? Example Class Definitions ``` setRefClass("B", field=list( b.1="numeric", b.2="logical" ), methods=list( thisIsPublic=function(...) { thisIsPublic_ref(.self=.self, ...) }, thisIsPrivate=function(...) { thisIsPrivate_ref(.self=.self, ...) } ) ) setRefClass("A", field=list( a.1="B" ) ) ``` NOTE I usually do not place the actual method definition within the class def but separate it to a S4 method (i.e. `thisIsPublic_ref`) for the following reasons: - That way the class def stays clearly arranged and is easier to read in cases when the individual method defs grow quite large. - It allows you to switch to a functional execution of methods at any time. Be `x` an instance of a certain class, you are able to call `foo_ref(.self=x)` instead of `x$foo()`. - It allows you to byte-compile the methods via `compiler::cmpfun()` which I think is not possible if you have "plain" Reference Class methods. It sure does not really make sense to make it that complicated for this specific example, but I thought I'd nevertheless illustrate that approach. Method Definitions ``` setGeneric( name="thisIsPublic_ref", signature=c(".self"), def=function( .self, ... ) { standardGeneric("thisIsPublic_ref") } ) setGeneric( name="thisIsPrivate_ref", signature=c(".self"), def=function( .self, ... ) { standardGeneric("thisIsPrivate_ref") } ) require(compiler) setMethod( f="thisIsPublic_ref", signature=signature(.self="B"), definition=cmpfun(function( .self, ... ){ .self$b.1 * 1000 }) ) setMethod( f="thisIsPrivate_ref", signature=signature(.self="B"), definition=cmpfun(function( .self, ... ){ .self$b.2 }) ) ``` Instances ``` x.b <- new("B", b.1=10, b.2=TRUE) x.a <- new("A", a.1=x.b, a.2="hello world") ``` Public vs. private Instances of class `A` (i.e. `x.a`) should be allowed to use class `B`'s public methods: ``` > x.a$a.1$thisIsPublic() [1] 10000 ``` Instances of class `A` (i.e. `x.a`) should not be allowed to use class `B`'s private methods. So I would want this not to work, i.e. result in an error: ``` > x.a$a.1$thisIsPrivate() [1] TRUE ``` Any idea how one could specify this? The only thing I came up with so far: Adding a `sender` argument to each method, explicitly specify it for each method call and check if `class(.self) == class(sender)`. But that seems a bit “explicit“.