Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make variable available to namespace at loading time

In one of my packages I use the .onAttach hook to run some R code and then use assign to make the value available as one of the package variables. I do it because variable depends on the content of some file, which can change between one session and the other. The code I use is like:

.onAttach <- function(libname, pkgname) {
   variable <- some_function()
   assign("variable", variable, envir = as.environment("package:MyRPackage"))
}

When I attach the package with library(MyRpackage) I can use variable.

However it is not possible to do something like MyRPackage::variable (unless I have already attached the package with library(MyRpackage).

I know this is because I should put that code in the .onLoad hook, however I can't make the assignment so that it works.

I have tried

.onLoad <- function(libname, pkgname) {
   variable <- some_function()
   assign("variable", variable, envir = as.environment("namesoace:MyRPackage"))
}

and

.onLoad <- function(libname, pkgname) {
   variable <- some_function()
   assign("variable", variable, envir = asNamespace("MyRPackage"))
}

but both of them fail with some error when I run MyRPackage:::variable without using library to attach the package.

What is the correct to do the assignment in the .onLoad hook?

like image 550
lucacerone Avatar asked Mar 01 '18 18:03

lucacerone


2 Answers

There are essentially three ways:

  1. via assignInMyNamespace(…)
  2. via assign(…, envir = topenv())
  3. via subset-assign: ns$name = value

Although option 1 seems to be quite widespread, it actually requires more code, because you’ll first need to create a variable before you can overwrite it via assignInMyNamespace:

myvar = NULL

.onLoad = function (libname, pkgname) {
    assignInMyNamespace('myvar', value)
}

Failure to pre-declare the variable will lead to an error.

By contrast, assign is perfectly capable of creating a new variable which hasn’t been declared before. We just have to tell R into which environment to assign the variable, and the function topenv() provides this.

.onLoad = function (libname, pkgname) {
    assign('myvar', value, envir = topenv())
}

And of course we don’t need assign() (or assignInMyNamespace()) if we define a namespace object and subset-assign into it:

.onLoad = function (libname, pkgname) {
    ns = topenv()
    ns$myvar = value
}

For my own code I am gravitating towards the last option.

like image 166
Konrad Rudolph Avatar answered Nov 09 '22 01:11

Konrad Rudolph


Following the approach in this answer to a related question, you can change your .onLoad() function like so:

.onLoad <- function(libname, pkgname) {
    variable <- some_function()
    assign("variable", variable, envir = parent.env(environment()))
}

Then you can access variable without attaching the package using MyRPackage:::variable. I don't know what you do with some_function(), so I tried the following with a dummy package:

.onLoad <- function(libname, pkgname) {
    variable <- 42
    assign("variable", variable, envir = parent.env(environment()))
}

And in a fresh R session the result was

> MyRPackage:::variable
[1] 42

Further explanation

From Hadley Wickham's Advanced R:

There are four special environments:

...

  • The environment() is the current environment.

...

You can list the bindings in the environment’s frame with ls() and see its parent with parent.env().

So, if we modify the .onLoad() function further, we can see this in action:

.onLoad <- function(libname, pkgname) {
    print(environment()) # For demonstration purposes only;
    print(parent.env(environment())) # Don't really do this.
    variable <- 42
    assign("variable", variable, envir = parent.env(environment()))
}

Then starting an R session results in

<environment: 0x483da88>
<environment: namespace:MyRPackage>

being printed to the console at the start of the session. This allows you to assign variable in the environment namespace:MyRPackage even though trying assign("variable", variable, envir = namespace:MyRPackage) would result in the error

Error: package or namespace load failed for ‘MyRPackage’:

  .onLoad failed in loadNamespace() for 'MyRPackage', details:

  call: get(name, envir = ns, inherits = FALSE)

  error: object 'namespace' not found

when installing the package.

like image 25
duckmayr Avatar answered Nov 09 '22 00:11

duckmayr