[Rd] Best style to organize code, namespaces

Mark.Bravington at csiro.au Mark.Bravington at csiro.au
Tue Feb 23 05:27:42 CET 2010


Ben--

FWIW my general take on this is:

 - Namespaces solve the collision issue.

 - Style 2 tends to make for unreadably long code inside Foo, unless the subfunctions are really short.

 - Style 3 is too hard to work with

 - So I usually use a variant on style 1:

################### Style 4 (mlocal-style) ############### 
Foo <-  function(x) { ....
   initialize.Foo()
}

initialize.Foo <- function( nlocal=sys.parent()) mlocal({
  ....
})

The 'mlocal' call means that code in the body of 'initialize.Foo' executes directly in the environment of 'Foo', or wherever it's called from-- it doesn't get its own private environment, and automatically reads/writes/creates variables in 'Foo'. However, you can still pass parameters that are private to 'initialize.Foo', though you may not need any. The 'debug' package will handle 'mlocal' functions without any trouble. One downside might be that you can't (or shouldn't) call 'initialize.Foo' directly. Another is if your sub-function creates a lot of junk variables that you really don't want in 'Foo'-- obviously that's exactly what you want from an initialization function, but not necessarily in general.

 - Sometimes (style 5) I define the subfunctions externally to 'Foo' but not as 'mlocal's, and then inside 'Foo' I do

subf <- subf
environment( subf) <- environment()

just as if I'd inserted the definition of 'subf' into 'Foo'. This is like style 2, but keeps the 'Foo' code short, and lets me set up debugging externally.

 - If you use style 2, you can still automatically set up the 'debug' package's debugging on 'Subf' by:

mtrace( Foo)
bp( fname='Foo', 1, FALSE) # don't stop at line 1
bp( fname='Foo', 2, { mtrace( Subf); FALSE}) #  set the breakpoint in 'Subf,' and then carry on in 'Foo' without stopping

You won't have to intervene manually when 'Foo' runs. However, this may slow down 'Foo' itself, and does require you to know a line number after the definition of 'Subf'.

No doubt there are many other approaches...

Mark

-- 
Mark Bravington
CSIRO Mathematical & Information Sciences
Marine Laboratory
Castray Esplanade
Hobart 7001
TAS

ph (+61) 3 6232 5118
fax (+61) 3 6232 5012
mob (+61) 438 315 623

Ben wrote:
> Hi all,
> 
> I'm hoping someone could tell me what best practices are as far as
> keeping programs organized in R.  In most languages, I like to keep
> things organized by writing small functions.  So, suppose I want to
> write a function that would require helper functions or would just be
> too big to write in one piece.  Below are three ways to do this:    
> 
> 
> ################### Style 1 (C-style) ############### Foo <-
>   function(x) { ....
> }
> Foo.subf <- function(x, blah) {
>   ....
> }
> Foo.subg <- function(x, bar) {
>   ....
> }
> 
> ################### Style 2 (Lispish?) ############## Foo <-
>   function(x) { Subf <- function(blah) {
>     ....
>   }
>   Subg <- function(bar) {
>     ....
>   }
>   ....
> }
> 
> ################### Object-Oriented ################# Foo <-
>   function(x) { Subf <- function(blah) {
>     ....
>   }
>   Subg <- function(bar) {
>     ....
>   }
>   Main <- function() {
>     ....
>   }
>   return(list(subf=subf, subg=subg, foo=foo)) } ###################
> End examples #################### 
> 
> Which of these ways is best?  Style 2 seems at first to be the most
> natural in R, but I found there are some major drawbacks.  First, it
> is hard to debug.  For instance, if I want to debug Subf, I need to
> first "debug(Foo)" and then while Foo is debugging, type
> "debug(Subf)".  Another big limitation is that I can't write
> test-cases (e.g. using RUnit) for Subf and Subg because they aren't
> visible in any way at the global level.      
> 
> For these reasons, style 1 seems to be better than style 2, if less
> elegant.  However, style 1 can get awkward because any parameters
> passed to the main function are not visible to the others.  In the
> above case, the value of "x" must be passed to Foo.subf and Foo.subg
> explicitly.  Also there is no enforcement of code isolation (i.e.
> anyone can call Foo.subf).     
> 
> Style 3 is more explicitly object oriented.  It has the advantage of
> style 2 in that you don't need to pass x around, and the advantage of
> style 1 in that you can still write tests and easily debug the
> subfunctions.  However to actually call the main function you have to
> type "Foo(x)$Main()" instead of "Foo(x)", or else write a wrapper
> function for this.  Either way there is more typing.     
> 
> So anyway, what is the best way to handle this?  R does not seem to
> have a good way of managing namespaces or avoiding collisions, like a
> module system or explicit object-orientation.  How should we get
> around this limitation?  I've looked at sample R code in the
> distribution and elsewhere, but so far it's been pretty
> disappointing---most people seem to write very long, hard to
> understand functions.      
> 
> Thanks for any advice!


More information about the R-devel mailing list