2 min read

Testing understanding of R package environments and lexical scoping

I am writing this quick post to capture some exploration I did in confirming my understanding of how R package environments work. Hadley Wickham’s Advanced R book was very helpful in understanding these topics and inspiring the tests I ran.

I am finishing up chapter 5 of Software for Data Analysis, which covers R objects. It had me thinking about environments; in particular, how R package environments are a bit special with regard to the namespace mechanism.

The namespace mechanism involves the exported functions in an R package sharing a namespace environment, which itself has a few key properties:

  1. It might contain other, non-exported objects, which are internal package functions/objects. This is less important than the next point.
  2. Its parent is the imports namespace—containing objects imported by the package—whose parent is the base namespace (not the base package environment), whose parent is then the global environment (whose parentage follows the search() ordering).

Importantly, the namespace of a function determines where it looks for objects that it uses. To test my understanding of this scoping, and the chain of parentage for a package, I made a quick R package anRpackage (using the handy function package.skeleton()) containing a single exported function foo():

foo <- function() bar

My thought was that since bar does not exist in the package, and my package has no imports, the usefulness (in terms of not erroring out) of the function relies on the existence of the object bar somewhere (most likely in the global environment, but possibly in a base package or other loaded package).

I though there was a chance that the package would not even build, but it did (perhaps it would not pass a CRAN check though). After installing, lo and behold, the following ensued:

library(anRpackage)
foo()
# Error in foo() : object 'bar' not found
bar <- 5
foo()
# [1] 5

Another interesting aspect was that the function is bound under its name (here, foo) in the package and namespace environments. The former is what is first hit when issuing foo, but the resultant function object’s binding environment is the namespace environment.

Two last things I want to study more here are the function vs. calling environments (I think this is basically creation time environment vs. call time environment) and function arguments. I think these are inherently intertwined.

1am :/