r-lib / R6

Encapsulated object-oriented programming for R
https://R6.r-lib.org
Other
404 stars 56 forks source link

assinging <- to active bindings when refering to a package with :: #169

Closed s-fleck closed 5 years ago

s-fleck commented 5 years ago

Assigning to active bindings does not work when one refers to objects from packages with ::. I am not sure if this is something you can fix, and it's a bit hard to produce a reprex, but I thought i'll report it here anyways.

> library(yog)
> yog::yog$threshold
[1] 0
> yog::yog$threshold <- 10
Error in loadNamespace(name) : there is no package called ‘*tmp*’
> yog$threshold <- 10
# works

Traceback

1: `*tmp*`::yog
 2: getExportedValue(pkg, name)
 3: asNamespace(ns)
 4: getNamespace(ns)
 5: tryCatch(loadNamespace(name), error = function(e) stop(e))
 6: tryCatchList(expr, classes, parentenv, handlers)
 7: tryCatchOne(expr, names, parentenv, handlers[[1]])
 8: value[[3]](cond)
 9: stop(e)
10: (function () 
{
    .rs.breakOnError(FALSE)
})()
11: .rs.breakOnError(FALSE)
12: eval(substitute(browser(skipCalls = pos), list(pos = (length(sys.frames()) - frame) + 2)), envir = sys.frame(frame))
13: eval(substitute(browser(skipCalls = pos), list(pos = (length(sys.frames()) - frame) + 2)), envir = sys.frame(frame))
wch commented 5 years ago

I think this is a limitation of :: -- unlike the $ and [[, the thing on the left doesn't refer to an actual object. For example:

ggplot2::diamonds <- 100
#> Error in ggplot2::diamonds <- 10 : object 'ggplot2' not found

Here, ggplot2 is not an R object. But if you do:

x <- list()
x$y <- 100

This works fine, because x does refer to an object.

The *tmp* variable comes from subset assignment, as in $<-, ::<-, or any other f<-: https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Subset-assignment

When you do ggplot2::diamonds <- 10, that's syntactically equivalent to `::<-`(ggplot2, diamonds). For demonstration purposes, I'll use a f<-, since it's equivalent, but a bit clearer than ::<-.

If you do:

f(ggplot2, diamonds) <- 10

It is equivalent to:

`*tmp*` <- ggplot2
ggplot2 <- "f<-"(`*tmp*`, diamonds, value=10)
rm(`*tmp*`)

So the following behavior isn't a surprise:

f(ggplot2, diamonds) <- 10
#> Error in f(ggplot2, diamonds) <- 10 : object 'ggplot2' not found

The first step, `*tmp*` <- ggplot2 fails, because there's no ggplot2 object.

If we create an object named ggplot2, it makes it through the *tmp* assignment, but then gives an error on the next step. (This is like your example, where the package and object are both named yog.)

ggplot2 <- 1
f(ggplot2, diamonds) <- 10
#> Error in f(ggplot2, diamonds) <- 10 : could not find function "f<-"

So it made it to the next step, where it tries to call f<-, but there was no such function.

In the example you provided, it actually gave a different error: Error in loadNamespace(name) : there is no package called ‘*tmp*’. I believe this is because of the nested assignment calls -- it involves both ::<- and $<-.

s-fleck commented 5 years ago

Hmm thanks for the quick reply. I will probably have to go back to use set_ and _get methods instead of active bindings :/