iuke-tier@ey m@iii@g oii uiow@@edu
2022-Feb-24 13:22 UTC
[Rd] [External] DOCS/BUG?: opts <- base::.Options is *not* a copy
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. It does not say that referencing the object makes a copy. So there is no inconsistency. 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. 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
Henrik Bengtsson
2022-Feb-24 19:21 UTC
[Rd] [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