Motivation
I transformed my Shiny app, imfweo, into a R package. GitHub repo is here. In the process, I fell into trouble, as I didn’t understand the namespace issues. So I did some experiments, using the monthApp example in “Matering Shiny”, and reading the Chapter 7.4.3 Namespaces in “Advanced R”. Both books are written by Hadley Wickham. As always, thank you, Hadley. I write this post which shows my understanding as of today to help future me.
Namespace issues
“Where does a function find non-argument variables, like a function name and a data name?” I call this namespace issues.
Experiments using the monthApp example in “Matering Shiny”
- A single file: Ctl + Shift + Enter, it works fine, and Global Environment is empty.
- A single file: Ctl + Enter, eack line works fine, and Global Environment is filled with function names like
birthstoneServer
and a data name likestones
This experiment suggests that the usual way, Ctl + Shift + Enter in app.R, runs in a function execution environment, not in Global Environment. Let us call this environment monthApp execution environment, even if monthApp is not yet here. I guess the file which contains shinyApp(ui, server)
is so special that it creates a function which encloses all the codes.
- A single file: Change
stones
tounits
, and Ctl + Shift + Enter, it still works.
Don’t forget to restart R at each experiment. Note that units
is a function name in Base Environment. As birthstoneServer
function is defined in monthApp execution environment, it can find units
there.
- Module files: Ctl + Shift + Enter, and you will see “Error: object ‘stones’ not found”
- Module files: Ctl + Enter,
stones <- vroom::
line, and createstones
in Global Environment. And then, Ctl + Shift + Enter, it works fine. - Module files: Change
stones
tounits
, Ctl + Shift + Enter, and you will see “Error: object of type ‘closure’ is not subsettable”. - Module files: Then, create
units
in Global Environment, it works fine.
Let us call the environment in which variables, such as birthstoneServer
function, are defined, R directory environment: monthApp execution environment is its ephemeral child environment, and Global Environment is its parent environment. Look at the last figure in Chapter 7.4.3 Namespaces in “Advanced R”, and insert R directory environment left to Global Environment. As there is no namespace: R directory environment, birthstoneServer
function searches stones
or units
only in the lower row of the figure. If stones
or units
is in Global Environment, it can find there. If not, it can’t find stones
, or can find units
as a function in Base Environment.
- Module files: Create a new file
stones.R
and movestones <- vroom::
line there, and it works fine. - Module files: Change
stones
tounits
, create a new fileunits.R
and moveunits <- vroom::
line there, and it works fine.
In this case, stones
or units
and birthstoneServer
exist in the same R directory environment, so birthstoneServer
can find stones
or units
there before searching Base Environment, or even Global Environment.
- A package: Leave
stones <- vroom::
line in monthApp: “Error: object ‘stones’ not found” - A package: Leave
stones <- vroom::
line in monthApp: Then createstones
in Global Environment, and it works fine. - A package: Leave
stones <- vroom::
line in monthApp: Changestones
tounits
, and “Error: object of type ‘closure’ is not subsettable” - A package: Leave
stones <- vroom::
line in monthApp: Changestones
tounits
, and createunits
in Global Environment. Still, “Error: object of type ‘closure’ is not subsettable”
Now it is a package. Let us call it monthApp package. Insert package: monthApp
right to Global Environment, and namespace: monthApp
left to namespace: stats
in the same last figure in Chapter 7.4.3 Namespaces in “Advanced R”. birthstoneServer
function searches from the top left. If stones
is in Global Environment, it finds there, and if not, it can’t find. Whether or not units
is in Global Environment, it finds units
as a function in namespace: base
in the top right before searching Global Environment.
- A package: Take optional extra, and create
data/stones.rda
: It works fine. - A package: Take optional extra, and create
data/units.rda
: “Error: object of type ‘closure’ is not subsettable”
Taking optional extra to create a package dataset is to make sure stones
or units
is in Global Environment. So the results are the same as above in case stones
or units
exists in Global Environment.
For best practices
I don’t know what are the best practices at this moment. Joe Cheng recommends to preprocess out of Shiny what all Shiny users do in this Youtube lecture. As for imfweo
package, I followed his recommendation, and created a lot of .rda files in data directory. units.rda
happened to be there, and I faced “Error: object of type ‘closure’ is not subsettable”. As a solution, I first created a list, like weo$meta$units
, and hoped weo
doesn’t match any namespace before Global Environment. In the second thought, as I update data every half a year and can compare the current one with the previous one in rolling, I decided to pass every data variable as an argument. As result, I can be sure there will be no accidental match in namespaces, but the codes have become more difficult to read, as there are so many non-reactive arguments.
I am not a developer but a practitioner. Environments are hard to understand. Future me, if you are confused, come back to this post.