Search code examples
renvironmentrlang

Updating a list object in the global environment from within a function in R


I am trying to update a global list from inside a function.

Here is the code that does not work (can be sourced as a whole file):

require(rlang)
(my_list <- list(a = 1, b = "two", c = "set outside"))

print( paste("my_list$c is" , my_list$c) )

my_function <- function(x = 1, y = 2, parent_object_name = "my_list") {
  z <- x + y # do some stuff (irrelevant here)

  some_names <- "updated inside"
  upper_env_object_name <- paste0(parent_object_name, "$c")
  # browser()
  # env_poke(env = env_tail(), upper_env_object_name, some_names) # does not work
  # env_poke(env =  env_parents()[[1]], upper_env_object_name, some_names) # does not work
  
  env_poke(env = caller_env(), upper_env_object_name, some_names ) # creates `my_list$c` character vector
  # force(env_poke(env = caller_env(), upper_env_object_name, some_names )) # creates `my_list$c` character vector
  # browser()
  # env_poke(env = caller_env(), paste0("as.list(",upper_env_object_name,")"), some_names) # creates as.list(my_list$c)` character vector

  return(z)
}

my_function(x = 1, y = 2, parent_object_name = "my_list")

print(class(`my_list$c`))
print( `my_list$c`) 

print( paste("my_list$c is" , my_list$c) )

I found this but it does not help: Updating a nested list object in the global environment from within a function in R

Tried also with assign, and specifying the environment.

Background: I have some S3- subclases and want to keep track of them in the parent class object, which is also a list. The subclass objects are created "on-demand" and I want to have an overview what was created. My workaround for now is to create a new vector in the global environment and update it with :

if (exists("global_names_list")) global_names_list <<- unique(rbind(global_names_list, some_names)) else global_names_list <<- some_names

Solution

  • This is probably not the best way, but it is a way:

    my_list <- list(a = 1, b = "two", c = "set outside")
    
    my_function <- function(x = 1, y = 2, parent_object_name = "my_list"){
      z <- x+y
      
      some_names <- "updated inside"
      lst <- get(parent_object_name)
      lst$c <- some_names
      assign(parent_object_name, lst, envir = .GlobalEnv)
      
      return(z)
      
    }
    
    my_function()
    #> [1] 3
    
    #check
    my_list
    #> $a
    #> [1] 1
    #> 
    #> $b
    #> [1] "two"
    #> 
    #> $c
    #> [1] "updated inside"