[R-pkg-devel] Is there a better way ...?

Martin Maechler m@ech|er @end|ng |rom @t@t@m@th@ethz@ch
Thu Oct 21 15:04:22 CEST 2021


>>>>> Duncan Murdoch 
>>>>>     on Thu, 21 Oct 2021 08:09:02 -0400 writes:

    > I agree with almost everything Deepayan said, but would add one thing:
    > On 21/10/2021 3:41 a.m., Deepayan Sarkar wrote:
    > ...

    >> My suggestion is having a package-specific environment, and Duncan's
    >> is to have a function-specific environment. If you only need this for
    >> this one function, then that should be good enough. If you eventually
    >> want to access the persistent information from multiple functions,
    >> having a package-specific environment would be more useful.

    > I agree with that statement, but those aren't the only two choices. 
    > Your local() call can create several functions and return them in a 
    > list; then just those functions have access to the local variables.  For 
    > example,

    > createFns <- local({

    > .fooInfo <- NULL

    > fn1 <- function (...) { ... }
    > fn2 <- function (...) { ... }

    > list(fn1 = fn1, fn2 = fn2)
    > })

    > fns <- createFns()
    > fn1 <- fns$fn1
    > fn2 <- fns$fn2

    > Now fn1 and fn2 are functions that can see .fooInfo, and nobody else can 
    > (without going through contortions).

    > One other difference between this approach and the package-specific 
    > environment:  there's only one package-specific environment in 
    > Deepayan's formulation, but I could call createFns() several times, 
    > creating several pairs of functions, each pair with its own independent 
    > version of .fooInfo.

    > I don't know if that's something that would be useful to you, but 
    > conceivably you'd want to maintain partial plots in several different 
    > windows, and that would allow you to do so.

Note that the above approach has been how  nls()  has been
implemented for R ... a very long time ago {before R 1.0.0}

e.g. from  example(nls) :

DNase1 <- subset(DNase, Run == 1)
fm1 <- nls(density ~ SSlogis(log(conc), Asym, xmid, scal), DNase1)
str(fm1 $ m)
> List of 16
>  $ resid     :function ()  
>  $ fitted    :function ()  
>  $ formula   :function ()  
>  $ deviance  :function ()  
>  $ lhs       :function ()  
>  $ gradient  :function ()  
>  $ conv      :function ()  
>  $ incr      :function ()  
>  $ setVarying:function (vary = rep_len(TRUE, np))  
>  $ setPars   :function (newPars)  
>  $ getPars   :function ()  
>  $ getAllPars:function ()  
>  $ getEnv    :function ()  
>  $ trace     :function ()  
>  $ Rmat      :function ()  
>  $ predict   :function (newdata = list(), qr = FALSE)  
>  - attr(*, "class")= chr "nlsModel"

## so 16 functions, all sharing the *same* environment very
## efficiently and nicely

## this is *the* environment for the fitted model :
fmE <- environment(fm1$m[[1]])
ls.str(fmE)
> convCrit : function ()  
> dev :  num 0.00479
> env : <environment: 0x106c88a0> 
> form : Class 'formula'  language density ~ SSlogis(log(conc), Asym, xmid, scal)
> getPars : function ()  
> ....     
> ....     
> ....     

so the environment "contains" the functions themselves (but quite
a few more things) and for an environment that means it only
has pointers to the same function objects which are *also* in  `fm1$m`.

So, there has been a nice convincing and important example on
how to do this - inside R for more than two decennia.

Martin Maechler



More information about the R-package-devel mailing list