rscoping

R scoping question: Object exists but I can't do anything with it


I'd like to feed a value from one function into another that I'm calling on inside the first, and I can't seem to get the scoping right. Crucially, the two functions are defined separately. Here's an example:

little_fun <- function(){
    print(paste("CheckNumber exists in parent frame =", exists("CheckNumber", where = parent.frame())))
    print(paste("CheckNumber exists in current frame =", exists("CheckNumber")))
    if(exists("CheckNumber", where = parent.frame())){
        print(CheckNumber + 2)
    }
}

Running little_fun() by itself returns

[1] "CheckNumber exists in parent frame = FALSE"
[1] "CheckNumber exists in current frame = FALSE"

which is what I expected. However, I want to make a more complicated function that calls on little_fun internally.

big_fun <- function(y){
    CheckNumber <- 5
        little_fun()
}

Calling on big_fun returns this:

[1] "CheckNumber exists in parent frame = TRUE"
[1] "CheckNumber exists in current frame = FALSE"
 Error in print(CheckNumber + 2) : object 'CheckNumber' not found

It makes sense to me that CheckNumber exists in the parent frame but not in the current frame. But how can CheckNumber exist in the parent frame but not be available for adding to 2? I thought that R would keep climbing the environment tree until it found the variable it needed. What's going on here?


Solution

  • The catch is that CheckNumber exists in the parent frame (from where little_fun is called) but not in the parent environment (where little_fun is defined).

    test with additional code in little_fun:

    little_fun <- function(){
        print(paste("CheckNumber exists in parent frame =",
                    exists("CheckNumber", where = parent.frame())))
        ## present in parent environment?
        print(paste("CheckNumber exists in parent environment =",
                    exists("CheckNumber", where = parent.env(environment()))))
        print(paste("CheckNumber exists in current frame =", exists("CheckNumber")))
        if(exists("CheckNumber", where = parent.frame())){
            print(CheckNumber + 2)
        }
    }
    

    To make CheckNumber available, define it in the same or a higher level environment as little_fun, not in a sibling environment (big_fun is a sibling of little fun inside the global environment, unless you e.g. define little_fun inside big_fun).

    Anyhow, supplying the value as a function argument– little_fun(CheckNumber = 5)–will prevent functions groping around in parent environments for same-named variables. Functions depending on variables apart from their function arguments are not easy to re-use for other code.

    (Background explanation in Chapter 7 "Environments" of Hadley Wickhams Advanced R.)