How to define hidden global variables inside R packages?
Thank you for sharing your packages @Dirk Eddelbuettel
The solution for my question is the following:
.pkgglobalenv <- new.env(parent=emptyenv())
exs.time.start<-function(){
assign("exs.time", proc.time()[3], envir=.pkgglobalenv)
return(invisible(NULL))
}
exs.time.stop<-function(restartTimer=TRUE){
if(exists('exs.time',envir=.pkgglobalenv)==FALSE){
stop("ERROR: exs.time was not found! Start timer with exs.time.start")
}
returnValue=proc.time()[3]-.pkgglobalenv$exs.time
if(restartTimer==TRUE){
assign("exs.time", proc.time()[3], envir=.pkgglobalenv)
}
message(paste0("INFO: Elapsed time ",returnValue, " seconds!"))
return(invisible(returnValue))
}
- I've created an environment with
new.env()
, inside my R file, before my function definitions. - I've used
assign()
to access the environment and change the value of my global variable!
The variable is hidden and everything works fine! Thanks guys!
Global variable in a package - which approach is more recommended?
Some packages use hidden variables (variables that begin with a .
), like .Random.seed
and .Last.value
do in base R. In your package you could do
e <- new.env()
assign(".sessionId", "xyz123", envir = e)
ls(e)
# character(0)
ls(e, all = TRUE)
# [1] ".sessionId"
But in your package you don't need to assign e
. You can use a .onLoad()
hook to assign the variable upon loading the package.
.onLoad <- function(libname, pkgname) {
assign(".sessionId", "xyz123", envir = parent.env(environment()))
}
See this question and its answers for some good explanation on package variables.
Define Global Variables when creating packages
There are standard ways to include data in a package - if you want some particular R object to be available to the user of the package, this is what you should do. Data is not limited to data frames and matrices - any R object(s) can be included.
If, on the other hand, your intention was to modify the global environment every time a a function is called, then you're doing it wrong. In R's functional programming paradigm, functions return objects that can be assigned into the global environment by the user. Objects don't just "appear" in the global environment, with the programmer hoping that the user both (a) knows to look for them and (b) didn't have any objects of the same name that they wanted to keep (because they just got overwritten). It is possible to write code like this (using <<-
as in your question, or explicitly calling assign
as in @abhiieor's answer), but it will probably not be accepted to CRAN as it violates CRAN policy.
Global variables in packages in R
In general global variables are evil. The underlying principle why they are evil is that you want to minimize the interconnections in your package. These interconnections often cause functions to have side-effects, i.e. it depends not only on the input arguments what the outcome is, but also on the value of some global variable. Especially when the number of functions grows, this can be hard to get right and hell to debug.
For global variables in R see this SO post.
Edit in response to your comment:
An alternative could be to just pass around the needed information to the functions that need it. You could create a new object which contains this info:
token_information = list(token1 = "087091287129387",
token2 = "UA2329723")
and require all functions that need this information to have it as an argument:
do_stuff = function(arg1, arg2, token)
do_stuff(arg1, arg2, token = token_information)
In this way it is clear from the code that token information is needed in the function, and you can debug the function on its own. Furthermore, the function has no side effects, as its behavior is fully determined by its input arguments. A typical user script would look something like:
token_info = create_token(token1, token2)
do_stuff(arg1, arg2, token_info)
I hope this makes things more clear.
How to view internal variables of an R package within an R session?
You can get all of the functions (exported and unexported) with ls
and asNamespace
:
head(ls(envir = asNamespace('data.table')))
# [1] "-.IDate" ":=" "[.data.table" "[.ITime"
# [5] "[<-.data.table" "[<-.IDate"
I'm not positive about your latter point, but I thing system.file
has what you want. IIRC anything else that gets installed with the package should be in this location.
head(list.files(system.file(package = 'data.table'), recursive = TRUE))
# [1] "DESCRIPTION" "help" "html" "INDEX" "libs"
# [6] "LICENSE"
Loading object into global environment in R Package using .onLoad()
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:
- Edit an R file.
- Press Ctrl/Cmd + Shift + L.
- Explore the code in the console.
- 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
Related Topics
R: How to Prompt The User for Input from The Console
Control The Fill Order and Groups for a Ggplot2 Geom_Bar
How to Uninstall R Completely from Os X
R: Gradient Fill for Geom_Rect in Ggplot2
Split Line by Multiple Points Using Sf Package
Column Name with Brackets or Other Punctuations for Dplyr Group_By
How to Append R Data Frame into Existing Excel Without Overwriting
Label_Parsed of Facet_Grid in Ggplot2 Mixed with Spaces and Expressions
Include Link to Local HTML File in Datatable in Shiny
R: As.Posixct Timezone and Scale_X_Datetime Issues in My Dataset
Extract First N Digits from a String
Under What Circumstances Does R Recycle
How to Use Custom Cross Validation Folds with Xgboost