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

Deepayan Sarkar deep@y@n@@@rk@r @end|ng |rom gm@||@com
Thu Oct 21 09:41:18 CEST 2021


On Thu, Oct 21, 2021 at 12:15 PM Rolf Turner <r.turner using auckland.ac.nz> wrote:
>
>
> On Thu, 21 Oct 2021 02:03:41 -0400
> Duncan Murdoch <murdoch.duncan using gmail.com> wrote:
>
> > On 21/10/2021 12:40 a.m., Andrew Simmons wrote:
> > > I think the simplest answer is to store the variable in the
> > > functions frame. I'm assuming here that the only plot.foo needs
> > > access to .fooInfo, if not this can be changed.
> > >
> > >
> > > plot.foo <- function (...)
> > > {
> > >      .fooInfo
> > > }
> > > environment(plot.foo) <- new.env()
> > > evalq({
> > >      .fooInfo <- NULL
> > > }, environment(plot.foo))
> > >
> > >
> > > Make your function, and do whatever you need with .fooInfo within
> > > said function. Whenever you previously updated .fooInfo in the
> > > global environment, update .fooInfo in plot.foo environment instead.
> > > Also, because .fooInfo is not stored in the package's frame, it
> > > won't be locked when the namespace is sealed. If you created it at
> > > the toplevel, that would create some issues. But this works fine.
> >
> > I agree with the final result, but I'd write the code differently:
> >
> > plot.foo <- local({
> >
> >    .fooInfo <- NULL
> >
> >    function (...) { ... }
> > })
> >
> > creates an environment, puts .fooInfo into it with value NULL, then
> > creates a function with that environment attached and returns it.
> >
> > I think Andrew's approach will work, but changing a function's
> > environment always worries me.  Using local(), the function assigned
> > to plot.foo never has a different environment than the one it ends up
> > with (and I don't need to remember how evalq() works).
>
> Thanks everyone for these suggestions.  They seem a great deal
> less shaganappi/kludgy than my previous approaches.
>
> I've never really felt totally comfortable with the environment
> concept, despite have used it quite a bit (basically in a
> hammer-and-hope style.)
>
> Can anyone comment on the difference between Deepayan's suggestion
> (create a new environment in the package) and Duncan's suggestion
> (create an environment that is local to plot.foo())?  Are there pros
> and cons between the two?

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'm not sure what you are trying to do, but I can't see how you can do
something sensible with a function-specific environment if someone
does

plot.foo(something)
plot.default(1:10)
plot.foo(something else, add = TRUE)

So maybe you would eventually want to set a hook (?setHook) for
plot.new to ensure that no other plot has been created in between,
which could write into this package-specific environment.

> And Deepayan:  what is the rationale for not exporting the new
> environment that you suggest creating?  Presumably this guards against
> something.  What?  I'd just like to extend my (currently minimal)
> comprehension of the issues.

Nothing other than the usual reason for not exporting things
unnecessarily, which is to not pollute the user workspace.

> I must admit that Andrew's suggestion kind of overwhelms and bewilders
> me.  I really have no idea what evalq() does.  I guess I could RTFM,
> but the thought of doing that scares me! :-)

Andrew's suggestion looks more complicated than it is. Think of
.fooInfo as a "global" variable, just in your package namespace rather
than .GlobalEnv, so you could do (in your package code)

.fooInfo <- NULL

plot.foo <- function(...)
{
   if (is.null(.fooInfo)) ... # use .fooInfo
   .fooInfo <<- something # set .fooInfo
}

Andrew suggested a separate (and unnamed) environment to store both
.fooInfo and plot.foo, so the setting part becomes a bit more
complicated (but accessing becomes safer in the sense that no other
function can access .fooInfo).

My suggestion is essentially similar, except that you can use <-
instead of <<- because it's an environment.

.fooEnv <- new.env()

plot.foo <- function(...)
{
   if (is.null(.fooEnv$info)) ... # use .fooEnv$info
   .fooEnv$info <- something # set .fooEnv$info
}

Best,
-Deepayan

> Thanks again everybody.
>
> cheers,
>
> Rolf
>
> --
> Honorary Research Fellow
> Department of Statistics
> University of Auckland
> Phone: +64-9-373-7599 ext. 88276
>



More information about the R-package-devel mailing list