Getting Started

{box} is an R package that released its very first v1.0.0 version on CRAN on February 2021. So, before we dive deeper, install {box} from CRAN first:

install.packages("box")

or you can install the development version from R-universe:

install.packages('box', repos = 'https://klmr.r-universe.dev')

Preferably, for the later chapters which involves the {carrier} Rust package, you’ll need the following forked version of this package, considering that the patches under the fork hasn’t made {box} upstream yet, which you can import {box}-{carrier} modules as you do with importing R packages and local modules. To install, do the following:

# Install the package through GitHub
# This needs compilation BTW
# Configure the following requirements 
# Like Rtools and some toolchains
# To build the package
# install.packages('pak')
pak::pak(
    "joshuamarie/box@feature/carrier-module-support"
)

Why Box

For a long time, most R users compose their R codes in an R package or just a local collection of R scripts and sometimes with a deep nested directory. The reusability is rather not that granular: you going to have to use library() to load R packages and source() to read R codes within and between scripts. As discussed in the introduction, they are mostly a problem in management, most especially when you code gets larger and larger. And as you can see, there’s 2 functions you’re going to use and there’s no singular solution for an easy and ergonomic reusability.

That’s why {box} R package is recommended for these because it brings and simplifies modular programming in R for us. As described in its official documentation, it provides two key benefits:

  1. It facilitates modular code writing by treating files and folders as independent, nestable modules, so you don’t need to wrap reusable code into packages.

  2. It introduces a more powerful, less error-prone syntax for importing code from packages or modules, enabling explicit control over what names to import and restricting their scope.

With {box}, R users (or useRs) can take advantage of a modern (and correct) module system approach in R.

Understanding {box} package functionality

Before starting, you need to understand the field of functions offered by this package first.

Writing modules

To write modules with {box} is like writing an R package without the need to switch to other R projects with special package annotation and few boilerplates.

TipDid you know?

You can treat modules like packages. You can take advantage of this package to make your package prototype, before converting this into an actual package. On the recent development of {carrier} in 2026, you can make a package out of those modules with it.

Here are the few primary functions to be used when writing modules with {box}:

  • box::use()
  • box::file()
  • box::name()
  • box::register_s3_method()
  • mod-hooks: Hooks for module events (see for more details)
  • Miscellaneous: Roxygen2 documentation, which is beneficial in writing documentation for the R code under the module.

Module import / usage

Not just local modules, this also includes R packages.

NoteHistory

Historically, box::use() is initially an alternative approach to import R packages.

TipNerdy Fact

{box} leverages R’s non-standard evaluation (NSE), by writing an expression as if they exist (only in different context). With {box}, you can refer R packages like list objects, then the brackets utilizes the “subset” functionality in R that extracts the exported namespaces: not through numbers and string, but through a literal name. No mapping and programmatic approach, aside from calling a name of the module with box::name(), you have to be explicit by naming the imports.


Understanding Module Paths and Root Directory

Before proceeding to the next step: importation, one crucial aspect of using {box} is understanding how it locates and imports modules. The package uses a root directory concept to resolve module paths:

  1. Root Directory: By default, box::file() searches for modules starting from your project’s root directory. Exceedingly, box::file() is also used to search the path within the script file you are working with.

  2. Module Path Resolution:

    • Paths starting with ./, e.g. ./module/file, are relative to the current file

    • Paths without leading dots, e.g. module/file, are relative to the root directory

    • Paths starting with ../ are relative to the parent directory of the current file

    • Forward slashes (/) are used, even on Windows

Further explanations in chapters 2 to 3.

Knowing this helps streamline module management with {box}.


Basic Usage Example

Let’s start with examples. Remember that modules are objects after script files, folders / subfolders, or packages are loaded by box::use().

To get started, start a project, with a root directory name of modl/ (a short abbreviation of “module”), then create a script name mod.r / mod.R in the directory from the assumed directory structure:

# modl/
# ├── mod.r

After creating mod.r / mod.R script, then try copy this code:

box::use(
    stats[lm]
)

fit_and_predict = function (data, formula, ...) 
    lm(formula, data, ...)

and then save the script. After you save the following script, you can now import mod as a module:

box::use(./mod)

There’s no .r suffix, the name after / refers to a callable module in a form of a directory or an R script. Again, no need to quote the arguments in box::use(), unlike library() which you are allowed to import packages with quotes. After all, {box} package leverages R’s NSE.

Let’s get deeper. How about this mod.r you just wrote found under much deeper subfolder or subdirectory? To call such script as a module, you are going 1 level deeper and call that subfolder/subdirectory name before /, then call the script. Let’s assume that this is the directory structure:

# modl/
# ├── folder1/
# │   ├──mod.r
# │   ├── ...this can get deeper 

To import the script as a module, do the following:

box::use(./folder1/mod)

Now, you have your mod.r script saved into the current environment as an environment called module.

Once called, you may now reuse it according to the following:

mod$fit_and_predict(cars, dist ~ speed)

Common Import Patterns

Now that you have written an R code, saved in a script called mod.r, you also learn to import it as a module. You can access its names through $ operator, but there are few (if not several) ways to import mod.r or qualify its names. Here are some typical module import scenarios:

  1. Import an entire script / folder as a module:

    box::use(
       ./mod
    )

    Note: If it is a script, you can directly access the names inside mod, i.e. mod$fobj. On the other hand, if mod is a folder or a directory, you need a file called __init__.r that qualifies the folder as a module, then you can access its names as what you did by importing a script, or else you have to qualify the scripts under mod folder i.e. ./mod/.../.... See Chapter 3 for more details.

  2. Import specific script in a folder as a module:

    box::use(
        ./mod/mod1
    )
  3. Import specific objects inside a script or an __init__.r-flavored folder being explicitly called as a module:

    box::use(
        ./mod[obj1, obj2]
    )

    If mod is a folder, it can have nested modules, or usually just R objects like functions.

  4. Putting an alias when importing mod script / folder:

    box::use(
        md = ./mod
    )
  5. Rename an object within the index, which treats mod like a list:

    box::use(
        ./mod[ob1 = obj1, ob2 = obj2]
    )
    Note

    Note: You don’t really have to put aliases for all the imports:

    box::use(
        ./mod[ob1 = obj1, obj2]
    )
  6. Import from folders / nested folders

    box::use(
        ./mod/mod1,                      # From subdir/module.R
        ./folder/mod/mod1,               # From deep/nested/module.R
        ./folder/mod/mod1[ob1 = obj1]   
    )

This is what other programming languages are proposing you to do. Known programming languages like Python strongly enforces this. The tradeoff is that it requires verbose syntax but the cost is minimal compared to what other solutions has offered.

NoteRemember

This will work if mod is a script, not a folder, unless it has an initialization file named __init__.r.


Troubleshooting Module Imports

These are trivial and common issues, and so the solutions are also trivial:

  1. When the module is not found, simply:

    • Verify path is relative to root directory

    • Ensure file exists with .R/.r extension

  2. For resolving the path of the modules:

    Missing required prefixes, e.g. ./, to import the local modules, and wrong syntax are invalid.

    box::use(modules)                  # Missing ./
    box::use(\modules\my_module)       # Wrong slashes
    NoteTo be discussed…

    It is important to know that you can import modules through box::use() only if modules is a package (this will be explain in Chapter 2).

    The / is a division operator in R, but box::use() parses it differently, where the left hand side is the relative directory and the right hand side is the module you are going to import.

    box::use(./modules/my_module)      # Relative to current file
    box::use(modules/my_module)        # Relative to root (if set)
  3. It is much better if you follow best Practices. One example is to use relative paths with ./ for clarity, if not a package. Keep local module files in a dedicated directory. And then, follow some consistent naming conventions, e.g. minimize using special characters that R parses unconventionally like a variable name of fn-1.


Common use of syntax

When using {box}, you are hereby using the package through box:: prefixes only. You are not allowed to attach {box} through library(), i.e. library(box), but this is the less of your worries since it is already automatically internally hard-coded.