R OOP – a little privacy please?
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
As of late, I’ve been making heavy use of Reference Classes in R. They are easier for me to wrap my mind around since they adopt a usage style more like “traditional” OOP languages like Java. Primarily, object methods are part of the class definition and accessed via the instantiated object.
For instance:
With S3/S4 classes, you define an object. Then you define separate generic functions that operate on the object:
<span class="com"># class and object method definition</span><span class="pln"><br />myClass </span><span class="pun">=</span><span class="pln"> setClass</span><span class="pun">(</span><span class="str">'myClass'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...)</span><span class="pln"><br /></span><span class="kwd">print</span><span class="pun">.</span><span class="pln">myClass </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">x</span><span class="pun">){...}</span><span class="pln"><br /><br /></span><span class="com"># so then ...</span><span class="pln"><br />obj </span><span class="pun">=</span><span class="pln"> myClass</span><span class="pun">(...)</span><span class="pln"><br /></span><span class="kwd">print</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">)</span>With Reference classes, you define an object and therein the methods the object employs:
<span class="com"># class and object method definition</span><span class="pln"><br />myClass </span><span class="pun">=</span><span class="pln"> setRefclass</span><span class="pun">(</span><span class="str">'myClass'</span><span class="pun">,</span><span class="pln"> <br /> fields </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(),</span><span class="pln"> <br /> methods </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="kwd">print</span><span class="pun">=</span><span class="kwd">function</span><span class="pun">(){...}))</span><span class="pln"><br /><br /></span><span class="com"># so then ...</span><span class="pln"><br />obj </span><span class="pun">=</span><span class="pln"> myClass</span><span class="pun">(...)</span><span class="pln"><br />obj$print</span><span class="pun">()</span>
In the grand scheme of things, both ways of defining objects and their methods are pretty much equivalent. From a coding perspective, the S3/S4 style allows for object methods to be defined separately of the object class (e.g. in separate files if one prefers). The appeal of Reference Classes is that the objects they define know what methods they have.
Privacy issues
The one aspect of OOP in R that I’ve been trying to work out is how to implement private methods and fields – i.e. only visible/usable from within the scope of the object, thus not user callable/mutable. There is (currently) no official way to specify these in base R.
A little research reveals that the best one can do is obfuscate. Roxygen2
will not specify the existence of a RefClass method if it lacks a docstring, but it will still be available to the user if they introspect the object interactively (ala the tab
key if using RStudio).
The best suggestions I’ve come across are:
- build a package around your RefClass definition and use non-exported package functions for private methods
- define private methods as functions within the public methods they are used in
Option 1 is probably the better of the two, as it lets R’s namespace rules do the dirty work. However, it does require writing functions of the form:
<span class="pln">privateFun </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com"># do stuff</span><span class="pln"><br /> obj$field </span><span class="pun"><<-</span><span class="pln"> newValue<br /></span><span class="pun">}</span>
Option 2 would likely require much code replication or, at the very least
source()
-ing the requisite code where ever a private function is required. A very far from ideal development/debugging situation.
From a high level view, Reference Classes are environments
with added bells and whistles. What’s interesting is say I defined a class like so:
<span class="pln">myClass </span><span class="pun">=</span><span class="pln"> setRefClass</span><span class="pun">(</span><span class="str">'myClass'</span><span class="pun">,</span><span class="pln"><br /> fields </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="pln"><br /> pubField </span><span class="pun">=</span><span class="pln"> </span><span class="str">'character'</span><span class="pun">,</span><span class="pln"><br /> </span><span class="pun">.</span><span class="pln">prvField </span><span class="pun">=</span><span class="pln"> </span><span class="str">'character'</span><span class="pln"><br /> </span><span class="pun">),</span><span class="pln"><br /> methods </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="pln"><br /> pubMethod </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(){</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'public'</span><span class="pun">)},</span><span class="pln"><br /> </span><span class="pun">.</span><span class="pln">prvMethod </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(){</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'private'</span><span class="pun">)}</span><span class="pln"><br /> </span><span class="pun">)</span><span class="pln"><br /></span><span class="pun">)</span>
Notice, that the “prvField” field and “prvMethod” use a
.
to prefix the name. In R this is a way of creating a “hidden” variable – akin to hidden files on a *nix OS.
When I try to ls()
the resultant object, I get:
<span class="pun">></span><span class="pln"> obj </span><span class="pun">=</span><span class="pln"> myClass</span><span class="pun">()</span><span class="pln"><br /></span><span class="pun">></span><span class="pln"> ls</span><span class="pun">(</span><span class="pln">env </span><span class="pun">=</span><span class="pln"> obj</span><span class="pun">)</span><span class="pln"><br /></span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="str">"getClass"</span><span class="pln"> </span><span class="str">"pubField"</span>
So, it is within the realm of possibilities!
Another alternative that I thought of was to make a field of the object an environment
and then place private elements there. Again, making things private via obfuscation (and more typing). However, users could still access said elements by:
obj$private$field
R6 – a new hope
As I was putting the finishing touches on this post I read a great post by Romain Francois entitled “Pro Grammar and Devel Hoper” (kudos on the pun). Towards the end he links to an Rpub posted by Winston Chang entitled “Introduction to R6” which piqued my interest.
R6 is a new OOP system provided by the R6 package – posted to CRAN just 4 days ago about a month ago. While similar to the existing Reference Class system (objects are specially wrapped environments), it also provides separation of public and private elements – exactly what I was looking for! Performance tests also show that R6 is faster and more memory efficient than Reference Classes.
Suffice it to say, I’ll be checking R6 out.
Written with StackEdit.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.