PR #103, which evolved into #191 (by @krlmlr and @wch) did not evaluate default arguments in the correct scope. For example, consider the following:
init_x <- 1
A <- R6Class("A", public = list(
initialize = function(x = init_x, y = self$init_y) {
self$x <- x
self$y <- y
},
x = NULL,
y = NULL,
init_y = 2
))
a <- A$new()
a$x # should be 1
a$y # should be 2
The defaults for x and y should be evaluated inside the initialize() functions execution environment, but the change in those PRs did not do that, and calling A$new() would throw an error, because the it tried to evaluate those expressions in the calling environment.
I've made a fix for that in abd8171, and also tried to pass along the "missingness" of arguments, but there's still a few more issues.
First, the positions of arguments don't always work correctly if there are empty arguments (just a comma with nothing before it). Here's an example:
A <- R6Class("A", public = list(
initialize = function(x = 1, y = 2, ..., z) {
self$x <- x
self$y <- y
self$dots <- list(...)
self$z <- if (missing(z)) 3 else z
},
x = NULL,
y = NULL,
z = NULL,
dots = list()
))
We would expect it to behave this way:
a <- A$new(,20,100)
# All of these are TRUE
identical(a$x, 1)
identical(a$y, 20)
identical(a$dots, list(100))
However, in abd8171, it behaves like this:
a <- A$new(,20,100)
# All of these are TRUE
identical(a$x, 100)
identical(a$y, 20)
identical(a$dots, list())
That is, the third argument, 100, gets treated as x.
The next problem involves nonstandard evaluation. In the CRAN version of R6, here's what happens if you call substitute() inside of the initializer -- it can capture the expression from the call to $new():
library(R6)
A <- R6Class("A", public = list(
initialize = function(expr) {
self$expr <- substitute(expr)
},
expr = NULL
))
a <- A$new(x+y+z)
identical(a$expr, quote(x+y+z)) # TRUE
This works because $new() simply takes ... as the only argument, and when $initialize() calls substitute(), the expr gets resolved back to the call to $new() -- that's a special property of using ....
In the dev version, here's what happens:
a <- A$new(x+y+z)
#> Error in A$new(x + y + z) : object 'x' not found
The substitute() doesn't know to go two levels back. I have a feeling this problem can't be solved, and so we'll have to revert the changes.
PR #103, which evolved into #191 (by @krlmlr and @wch) did not evaluate default arguments in the correct scope. For example, consider the following:
The defaults for
x
andy
should be evaluated inside theinitialize()
functions execution environment, but the change in those PRs did not do that, and callingA$new()
would throw an error, because the it tried to evaluate those expressions in the calling environment.I've made a fix for that in abd8171, and also tried to pass along the "missingness" of arguments, but there's still a few more issues.
First, the positions of arguments don't always work correctly if there are empty arguments (just a comma with nothing before it). Here's an example:
We would expect it to behave this way:
However, in abd8171, it behaves like this:
That is, the third argument,
100
, gets treated asx
.The next problem involves nonstandard evaluation. In the CRAN version of R6, here's what happens if you call
substitute()
inside of the initializer -- it can capture the expression from the call to$new()
:This works because
$new()
simply takes...
as the only argument, and when$initialize()
callssubstitute()
, theexpr
gets resolved back to the call to$new()
-- that's a special property of using...
.In the dev version, here's what happens:
The
substitute()
doesn't know to go two levels back. I have a feeling this problem can't be solved, and so we'll have to revert the changes.