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?
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:
x
an instance of a certain class, you are able to call foo_ref(.self=x)
instead of x$foo()
.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.
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 }) )
x.b <- new("B", b.1=10, b.2=TRUE) x.a <- new("A", a.1=x.b, a.2="hello world")
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“.
A private method has a body in an interface means that we can't be declared as a normal abstract method as usually do in an interface.
You can make methods private too. Object users can't use private methods directly. The main reason to do this is to have internal methods that make a job easier.
Generally you should expose as little as possible and make everything private that is possible. If you make a mistake and hide something you should be exposing, no problem, just make it public.
They are not allowed to be accessed directly by any object or function outside the class. Only the member functions or the friend functions are allowed to access the private data members of a class.
The short answer is to make a package. R's object systems and it's means of partitioning code (namespaces) are more separate than their equivalents in Java-like languages.
When you make a package, you specify what gets exported in a file called NAMESPACE using directives export
and exportMethods
. You can choose not to export methods and other R objects that you wish to be package private (to use Java terminology). See the Namespaces with S4 classes and methods section of the Writing R Extensions manual
Making a package is tricky the first time you do it, but there's lot's of help. See the docs for package.skeleton and the Writing R Extensions manual linked above.
Make sure Reference classes are really what you want. Regular S4 classes are usually the more R-ish way, for whatever that's worth. A great source of information about R's many OO constructs (and about packaging, too) is on Hadley Wickham's devtools wiki.
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()'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With