1
votes

I'm having a little trouble understanding why, in R, the two functions below, functionGen1 and functionGen2 behave differently. Both functions attempt to return another function which simply prints the number passed as an argument to the function generator.

In the first instance the generated functions fail as a is no longer present in the global environment, but I don't understand why it needs to be. I would've thought it was passed as an argument, and is replaced with aNumber in the namespace of the generator function, and the printing function.

My question is: Why do the functions in the list list.of.functions1 no longer work when a is not defined in the global environment? (And why does this work for the case of list.of.functions2 and even list.of.functions1b)?

functionGen1 <- function(aNumber) {
  printNumber <- function() {
    print(aNumber)
  }
  return(printNumber)
}

functionGen2 <- function(aNumber) {
  thisNumber <- aNumber
  printNumber <- function() {
    print(thisNumber)
  }
  return(printNumber)
}

list.of.functions1 <- list.of.functions2 <- list()
for (a in 1:2) {
  list.of.functions1[[a]] <- functionGen1(a)
  list.of.functions2[[a]] <- functionGen2(a)
}

rm(a)

# Throws an error "Error in print(aNumber) : object 'a' not found"
list.of.functions1[[1]]()

# Prints 1
list.of.functions2[[1]]()
# Prints 2
list.of.functions2[[2]]()

# However this produces a list of functions which work
list.of.functions1b <- lapply(c(1:2), functionGen1)
1

1 Answers

3
votes

A more minimal example:

functionGen1 <- function(aNumber) {
  printNumber <- function() {
    print(aNumber)
  }
  return(printNumber)
}

a <- 1
myfun <- functionGen1(a)
rm(a)
myfun()
#Error in print(aNumber) : object 'a' not found

Your question is not about namespaces (that's a concept related to packages), but about variable scoping and lazy evaluation.

Lazy evaluation means that function arguments are only evaluated when they are needed. Until you call myfun it is not necessary to evaluate aNumber = a. But since a has been removed then, this evaluation fails.

The usual solution is to force evaluation explicitly as you do with your functionGen2 or, e.g.,

functionGen1 <- function(aNumber) {
  force(aNumber)
  printNumber <- function() {
    print(aNumber)
  }
  return(printNumber)
}

a <- 1
myfun <- functionGen1(a)
rm(a)
myfun()
#[1] 1