In R, I wanted to create a class (R6Class) that when calling initialize creates few dynamic methods (the number of methods and its names depends on parameters in initialize). But I get into strange problem with environments.
Here is a simplified version of code that doesn't work.
library(R6)
ffactory <- function(i) {
function() i
}
A <- R6Class(
lock_objects=FALSE,
public=list(
initialize=function(args) {
for (i in args) {
self[[i]] <- ffactory(i)
}
}
)
)
a <- A$new(c('a', 'b', 'c'))
Now:
> a$a()
[1] "c"
> a$b()
[1] "c"
> a$c()
[1] "c"
In order to find what was wrong I had added a line that prints environment in ffactory function. That is
ffactory <- function(i) {
print(ls.str())
function() i
}
And now it has started to work!!!
> a$a()
[1] "a"
> a$b()
[1] "b"
> a$c()
[1] "c"
So why? There should be something I don't understand. Observer effect or what? :)
What is the magic of the line print(ls.str())
? Actually I cannot remove neither print
nor str
from this line. Of course it is so silly to have a line like that. Not to mention garbage on the screen.
You have encountered lazy evaluation - R waits as long as it is able to before evaluating i
- and in the former case, i
will be evaluated at its last value in all instances. There's nothing really special about the combination of print
and ls.str
; anything that forces i
to be evaluated prior to your method calls (a$a()
, a$b()
, etc...) will do the same.
Formally, this is what force
is used for:
ffactory <- function(i) {
force(i);
function() i
}
R> a$a()
#[1] "a"
R> a$b()
#[1] "b"
R> a$c()
#[1] "c"
However, this also happens to do the job:
ffactory <- function(i) {
#force(i);
.z <- capture.output(cat(i, "\n"))
function() i
}
R> a$a()
#[1] "a"
R> a$b()
#[1] "b"
R> a$c()
#[1] "c"
There are presumably countless ways to force evaluation; I would argue that using force
makes your intention most clear, though.
Quoting the help file directly,
force forces the evaluation of a formal argument. This can be useful if the argument will be captured in a closure by the lexical scoping rules and will later be altered by an explicit assignment or an implicit assignment in a loop or an apply function.
and subsequently,
This is semantic sugar: just evaluating the symbol will do the same thing (see the examples).
In fact, looking at how force
is defined,
R> force
#function (x)
# x
#<bytecode: 0x3b7b528>
#<environment: namespace:base>
You could even get away with
ffactory <- function(i) {
i; function() i
}
But as noted, I think the explicitly calling force
will make your code more readable.