[External] DOCS/BUG?: opts <- base::.Options is *not* a copy
On Thu, Feb 24, 2022 at 5:23 AM <luke-tierney at 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 at 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 at uiowa.edu
Iowa City, IA 52242 WWW: http://www.stat.uiowa.edu