Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to declare public and private methods for S4 Reference Classes?

Tags:

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:

  1. That way the class def stays clearly arranged and is easier to read in cases when the individual method defs grow quite large.
  2. 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().
  3. 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“.

like image 918
Rappster Avatar asked Jun 17 '12 17:06

Rappster


People also ask

Can private methods be declared?

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.

Can methods be public or private?

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.

Should class methods be public or private?

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.

Can a method be accessed outside the class?

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.


2 Answers

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.

like image 27
cbare Avatar answered Oct 10 '22 06:10

cbare


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()'.

like image 150
hd1 Avatar answered Oct 10 '22 04:10

hd1