Search code examples
rfunctionenvironment

In R, how do I create an environment in a function so that its child-functions can see it?


If I create the "outside" environment in the main R code below, both step1() and step2(), which is called from step1(), can see it. But if step1() creates an "inside" environment, step2() cannot see that. How do I create an environment in a function so that its child-functions can see it?

rm(list=ls())

step1 <- function() {
  ansout <- exists("outside", mode = "environment")
  ansin <- exists("inside", mode = "environment")
  cat("From step1, ansout = ", ansout, ", ansin = ", ansin, "\n")
  inside <- new.env()
  step2()
  return()
}

step2 <- function() {
  ansout <- exists("outside", mode = "environment")
  ansin <- exists("inside", mode = "environment")
  cat("From step2, ansout = ", ansout, ", ansin = ", ansin, "\n")
  return()
}

outside <- new.env()
step1()

# Output:
# From step1, ansout =  TRUE , ansin =  FALSE 
# From step2, ansout =  TRUE , ansin =  FALSE 

Solution

  • Functions enclose the environment where they’re defined. In other words, a function has access to objects that exist in the environment where it was defined, but doesn’t necessarily have access to objects that exist in the environment it’s called from.

    Your functions are both defined in the global environment, so only have access to outside. If you define step2() within step1(), it will have access to inside.

    step1 <- function() {
      step2 <- function() {
        ansout <- exists("outside", mode = "environment")
        ansin <- exists("inside", mode = "environment")
        cat("From step2, ansout = ", ansout, ", ansin = ", ansin, "\n")
        return()
      }
      ansout <- exists("outside", mode = "environment")
      ansin <- exists("inside", mode = "environment")
      cat("From step1, ansout = ", ansout, ", ansin = ", ansin, "\n")
      inside <- new.env()
      step2()
      return()
    }
    
    outside <- new.env()
    step1()
    # From step1, ansout =  TRUE , ansin =  FALSE 
    # From step2, ansout =  TRUE , ansin =  TRUE
    

    If nesting the function isn’t an option, you can add an argument to step2() to pass the inside environment as @SamR suggested. Or you can get() objects from the calling environment using parent.frame().

    step1 <- function() {
      ansout <- exists("outside", mode = "environment")
      ansin <- exists("inside", mode = "environment")
      cat("From step1, ansout = ", ansout, ", ansin = ", ansin, "\n")
      inside <- new.env()
      step2()
      return()
    }
    
    step2 <- function() {
      inside <- get("inside", envir = parent.frame())
      ansout <- exists("outside", mode = "environment")
      ansin <- exists("inside", mode = "environment")
      cat("From step2, ansout = ", ansout, ", ansin = ", ansin, "\n")
      return()
    }
    
    outside <- new.env()
    step1()
    # From step1, ansout =  TRUE , ansin =  FALSE 
    # From step2, ansout =  TRUE , ansin =  TRUE