Search code examples
roopenvironment-variablesr-packager6

Loading object into global environment in R Package using .onLoad()


I’m working on an R Package in which I need to manage the state of various objects over time. Conceptually, when the package loads (.onLoad), it checks for the state object in cache and if it doesn’t exist, a new instance is created, saved to cache, and assigned in the global environment. I’ve not been able to see the object in the global environment using .onLoad() after building the site using devtools :: build(). So, I have three questions:

  1. Is .onLoad() function the right place for this functionality? If so, what is the current best practice for making state variables visible in the global environment?
  2. Has a solution (package) for managing state across “R sessions” been developed?
  3. Is there a better conceptual approach to the problem than the one I’ve adopted?

Solutions tried...so far

I’ve scoured SE, read (and re-read) Hadley’s books on R Packages, and Advanced R, brooded over Winston Chang’s vignettes on R6 (links at bottom of post) and I’ve distilled my experiments down to three failed approaches. First, here’s a simple “GameClass” that instantiates a game with three variables, player 1, player 2, and state (of the game).

 #' GameClass
 #' \code{GameClass} Class that...#'
 #' @export
 GameClass <- R6::R6Class(
   "GameClass",
   public = list(
     player1 = character(0),
     player2 = character(0),
     state  = character(0),
     initialize = function(player1, player2) {
       self$player1 <- player1
       self$player2 <- player2
       self$state <- "1st Match"
     }
   )
 )

Approach 1

  Assign the variable to the global environment using the <<- operator


  .onLoad <- function(libname, pkgname) {

    gameFile <- "./gameFile.Rdata"
    if (file.exists(gameFile)) {
      game <<- load(gameFile)
    } else {
     game  <<- GameClass$new("Eric", "Cassie")
     save(game, file = gameFile)
    } 
  }

Approach 2:

Create a new environment and return it

  .onLoad <- function(libname, pkgname) {
    gameFile <- "./gameFile.Rdata"
    e <- new.env()

    if (file.exists(gameFile)) {
      e$game <- load(gameFile)
    } else {
      e$game <- GameClass$new("Eric", "Cassie")
      save(e$game, file = gameFile)
    }  
    e
  }

Approach 3:

  .onLoad <- function(libname, pkgname) {
    gameFile <- "./gameFile.Rdata"

    if (file.exists(gameFile)) {
      game <- load(gameFile)
    } else {
      game <- GameClass$new("Eric", "Cassie")
      save(game, file = gameFile)
    }
    assign("game", game, envir = .GlobalEnv)
  }

Session Info

 R version 3.4.1 (2017-06-30)
 Platform: x86_64-w64-mingw32/x64 (64-bit)
 Running under: Windows >= 8 x64 (build 9200)

 Matrix products: default

 locale:
 [1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United 
 States.1252    LC_MONETARY=English_United States.1252
 [4] LC_NUMERIC=C                           LC_TIME=English_United 
 States.1252    

 attached base packages:
 [1] stats     graphics  grDevices utils     datasets  methods   base     

 other attached packages:
 [1] R6Lab_0.1.0

 loaded via a namespace (and not attached):
 [1] compiler_3.4.1 R6_2.2.2       tools_3.4.1    yaml_2.1.14   

I"m new to OOP, new to R6, this is my first R package and I've been playing around with R for about a year. Clearly, I could benefit from some insight here.

Thanks in advance.

## References ##
[Hadley's Advanced R][1]
[Hadley's R Packages][2]
[Introduction to R6 Classes][3]
[How to define hidden global variables inside R Packages][4]
[Global variables in packages in r][5]
[Global variables in r][6]
[Global variable in a package which approach is more recommended][7]

  [1]: http://adv-r.had.co.nz/
  [2]: http://r-pkgs.had.co.nz/
  [3]: https://cran.r-project.org/web/packages/R6/vignettes/Introduction.html
  [4]: https://stackoverflow.com/questions/34254716/how-to-define-hidden-global-variables-inside-r-packages
  [5]: https://stackoverflow.com/questions/12598242/global-variables-in-packages-in-r
  [6]: https://stackoverflow.com/questions/1236620/global-variables-in-r
  [7]: https://stackoverflow.com/questions/28246952/global-variable-in-a-package-which-approach-is-more-recommended

Solution

  • There should be a word for looking for complex answers amidst obvious solutions. It could NOT have been more obvious.

    R code workflow

    The first practical advantage to using a package is that it’s easy to re-load your code. You can either run devtools::load_all(), or in RStudio press Ctrl/Cmd + Shift + L, which also saves all open files, saving you a keystroke. This keyboard shortcut leads to a fluid development workflow:

    1. Edit an R file.
    2. Press Ctrl/Cmd + Shift + L.
    3. Explore the code in the console.
    4. Rinse and repeat.

    Congratulations! You’ve learned your first package development workflow. Even if you learn nothing else from this book, you’ll have gained a useful workflow for editing and reloading R code

    Load_all(). Wow! Just that simple. Load all ran the .onload() function and rendered the objects into global environment. Who knew?

    Reference: R Code Workflow, R Packages, Hadley