2

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.

Bartek
  • 173
  • 1
  • 9

1 Answers1

3

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.

nrussell
  • 18,382
  • 4
  • 47
  • 60
  • Hehehe. Thanks a lot! It sort of funny to have lazy argument `i` inside lazy evaluated function `function() i` that is inside a non-lazy evaluated function. – Bartek Dec 09 '15 at 19:39
  • Sure, feel free to accept my answer if it solved your problem. – nrussell Dec 10 '15 at 01:48