[Rd] [External] DOCS/BUG?: opts <- base::.Options is *not* a copy

Henrik Bengtsson henr|k@bengt@@on @end|ng |rom gm@||@com
Thu Feb 24 20:21:05 CET 2022


On Thu, Feb 24, 2022 at 5:23 AM <luke-tierney using uiowa.edu> wrote:
>
> On Thu, 24 Feb 2022, Henrik Bengtsson wrote:
>
> > Hi, is the following a non-documented feature or a bug:
> >
> > $ R --quiet --vanilla
> > opts <- base::.Options
> > opts[["abc"]]
> > #> NULL
> > options(abc = 42)
> > opts[["abc"]]
> > #> [1] 42
> >
> > I would have expected that 'opts' would be a *copy* of base::.Options
> > that is not affected by later changes to base::.Options.  FWIW, the
> > same happens if we try with:
> >
> > opts <- .BaseNamespaceEnv[[".Options"]]
> >
> > I don't think lazy evaluation is involved, because I evaluate
> > opts[["abc"]] above.
> >
> > The only thing help(".Options") says is:
> >
> > Note:
> > For compatibility with S there is a visible object .Options whose
> > value is a pairlist containing the current options() (in no particular
> > order). Assigning to it will make a local copy and not change the
> > original. (Using it however is faster than calling options()).
>
> You are misreading what this says. As with any assignment to an object
> that has other references, the assignment will create a local copy
> before mutating.

I don't think I misread it; that's exactly how I interpreted it, too.
I just copied that passage to help the reader see that there's nothing
in the docs mentioning this behavior.

> It does not say that referencing the object makes a
> copy. So there is no inconsistency.

I'd argue that this behavior of .Options (because of how options() is
implemented) is not a standard procedure in R, and that the
expectation would be to make a copy unless otherwise documented. You
need to dig into the code to figure out that this is not the case and
how it works.  For example, my mental model of how this is implemented
is something like:

.Options <- pairlist()
options <- function(...) {
  args <- list(...)
  old <- new <- .Options
  for (name in names(args)) new[[name]] <- args[[name]]
  assignInMyNamespace(".Options", new)
  invisible(old)
}

and that does create a copy.

I only discovered this because I added the following to my package tests:

opts <- base::.Options
call_random_fcn()
stopifnot(identical(base::.Options, opts))

Turns out it never failed. I use .Options, because it's faster than
options(), which always sorts elements by their names.  FWIW, the
workaround is to force a copy using opts <- as.list(base::.Options).

> That options() modifies the value of an object with multiple
> references is not ideal, but changing that while maintaining .Object
> as a regular variable is probably more trouble than it is worth. It is
> probably time to work towards deprecating .Options (maybe turning it
> into an active binding that does make a copy). At the very least
> discouraging its use in the help file.

This sounds good to me.

Thanks,

Henrik

>
> Other that changing the docs I doubt this will ever get high enough on
> anyone's priority list to get done
>
> Best,
>
> luke
>
> >
> > This behavior goes back to at least R 2.15.0.
> >
> > /Henrik
> >
> > ______________________________________________
> > R-devel using r-project.org mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-devel
> >
>
> --
> Luke Tierney
> Ralph E. Wareham Professor of Mathematical Sciences
> University of Iowa                  Phone:             319-335-3386
> Department of Statistics and        Fax:               319-335-3017
>     Actuarial Science
> 241 Schaeffer Hall                  email:   luke-tierney using uiowa.edu
> Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu



More information about the R-devel mailing list