15

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?

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
lucacerone
  • 9,859
  • 13
  • 52
  • 80
  • 1
    Can you explain why you want to recompute variable each time during loading? – Dason Mar 01 '18 at 19:31
  • 1
    can you export `some_function` so that you can call `MyRPackage::some_function()`? – chinsoon12 Mar 02 '18 at 00:37
  • @Dason it has to read data from a file – lucacerone Mar 02 '18 at 06:16
  • @lucacerone is that file going to be changing every session or something? – Dason Mar 02 '18 at 13:10
  • yes Dason, it can change between one session and the other – lucacerone Mar 02 '18 at 16:16
  • I'm not sure you can use `MyRPackage:::variable` without loading the package before because the `variable` is assigned only when you actually load the package. Something I don't understand? – F. Privé Mar 04 '18 at 10:17
  • F. Privé I want to load the package.. I don't want to necessarily attach it.. for all the other exported functions you can do pkg::function, but for the one created in .onLoad I didn't manage to find a way – lucacerone Mar 04 '18 at 10:47

2 Answers2

20

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.

duckmayr
  • 16,303
  • 3
  • 35
  • 53
3

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.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214