All Projects → klmr → Box

klmr / Box

Licence: other
Write reusable, composable and modular R code

Programming Languages

r
7636 projects

Projects that are alternatives of or similar to Box

goproxy
go modules proxy. 墙内学习go语言的第一步 自建goproxy代理. 部署到 heroku
Stars: ✭ 22 (-94.33%)
Mutual labels:  modules, packages
Gomodctl
Search, Check, and Update Go modules.
Stars: ✭ 37 (-90.46%)
Mutual labels:  modules, packages
Mastering Modular Javascript
📦 Module thinking, principles, design patterns and best practices.
Stars: ✭ 3,972 (+923.71%)
Mutual labels:  modules, packages
Ndash
your npm dashboard! (react-native app)
Stars: ✭ 158 (-59.28%)
Mutual labels:  modules, packages
opensource
Collection of Open Source packages by Otherwise
Stars: ✭ 21 (-94.59%)
Mutual labels:  modules, packages
A To Z List Of Useful Node.js Modules
Collection of most awesome node modules that will extend the capability of your node.js application.
Stars: ✭ 315 (-18.81%)
Mutual labels:  modules, packages
Httpimport
Module for remote in-memory Python package/module loading through HTTP/S
Stars: ✭ 153 (-60.57%)
Mutual labels:  modules, packages
tcl-modules
A collection of pure Tcl, production-ready micro packages
Stars: ✭ 25 (-93.56%)
Mutual labels:  modules, packages
lint-deps
Lint for unused or missing dependencies in your node.js projects. Customize with plugins or configuration.
Stars: ✭ 48 (-87.63%)
Mutual labels:  modules, packages
Jdk9 Jigsaw
Examples and exercises based on some of the features of jigsaw in JDK9/Jigsaw (Early Access builds)
Stars: ✭ 275 (-29.12%)
Mutual labels:  modules
Framer Modules
Discover, install and save your favorite modules at one place
Stars: ✭ 338 (-12.89%)
Mutual labels:  modules
Package.elm Lang.org
website for browsing packages and exploring documentation
Stars: ✭ 271 (-30.15%)
Mutual labels:  packages
Flutter photo
Pick image/video from album by flutter. Support ios and android. UI by flutter, no native.
Stars: ✭ 285 (-26.55%)
Mutual labels:  packages
Moditect
Tooling for the Java Module System
Stars: ✭ 337 (-13.14%)
Mutual labels:  modules
Advanced Nodejs
For help, ask in #questions at slack.jscomplete.com
Stars: ✭ 273 (-29.64%)
Mutual labels:  modules
Repl
The Learning Hub for UoL's Online CS Students
Stars: ✭ 367 (-5.41%)
Mutual labels:  modules
Packages
List of packages for Nimble
Stars: ✭ 270 (-30.41%)
Mutual labels:  packages
Papi
🚀 WordPress Page Type API with custom fields
Stars: ✭ 258 (-33.51%)
Mutual labels:  modules
Termux Archlinux
You can use setupTermuxArch.bash 📲 to install Arch Linux in Termux on Amazon, Android, Chromebook and Windows. https://sdrausty.github.io/termux-archlinux/
Stars: ✭ 384 (-1.03%)
Mutual labels:  packages
Nod
Node.js module generator/boilerplate with Babel, Jest, Flow, Documentation and more
Stars: ✭ 355 (-8.51%)
Mutual labels:  modules

box

Write Reusable, Composable and Modular R Code

📦 Installation

‘box’ can be installed from CRAN:

install.packages('box')

Alternatively, the current development version can be installed from GitHub. Note that the main branch cannot be installed directly, since it intentionally misses generated files; instead, ‘box’ needs to be installed from the auto-generated dev branch:

if (! requireNamespace('pak') {
    install.packages('pak', repos = 'https://r-lib.github.io/p/pak/dev/')
}
pak::pkg_install('klmr/[email protected]')

🥜 Usage in a nutshell

‘box’ allows organising R code in a more modular way, via two mechanisms:

  1. It enables writing modular code by treating files and folders of R code as independent (potentially nested) modules, without requiring the user to wrap reusable code into packages.
  2. It provides a new syntax to import reusable code (both from packages and from modules) which is more powerful and less error-prone than library or require, by limiting the number of names that are made available.

Reusable code modules

Code doesn’t have to be wrapped into a package to be reusable. With ‘box’, regular R files form reusable R modules that can be used elsewhere. Just put the export directive #' @export in front of names that should be exported, e.g.:

#' @export
hello = function (name) {
    message('Hello, ', name, '!')
}

#' @export
bye = function (name) {
    message('Goodbye ', name, '!')
}

Such modules can be stored in a central module search path (configured via options('box.path')) analogous to the R package library, or locally in individual projects. Let’s assume the module we just defined is stored in a file hello_world.r inside a directory box, which is inside the module search path. Then the following code imports and uses it:

box::use(box/hello_world)

hello_world$hello('Ross')
#> Hello, Ross!

Modules are a lot like packages. But they are easier to write and use (often without requiring any set-up), and they offer some other nice features that set them apart from packages (such as the ability to be nested hierarchically).

For more information on writing modules refer to the Get started vignette.

Loading code

box::use is a universal import declaration. It works for packages just as well as for modules. In fact, ‘box’ completely replaces the base R library and require functions. box::use is more explicit, more flexible, and less error-prone than library. At its simplest, it provides a direct replacement:

Instead of

library(ggplot2)

You’d write

box::use(ggplot2[...])

This tells R to import the ‘ggplot2’ package, and to make all its exported names available (i.e. to “attach” them) — just like library. For this purpose, ... acts as a wildcard to denote “all exported names”. However, attaching everything is generally discouraged (hence why it needs to be done explicitly rather than happening implicitly), since it leads to name clashes, and makes it harder to retrace which names belong to what packages.

Instead, we can also instruct box::use to not attach any names when loading a package — or to just attach a few. Or we can tell it to attach names under an alias, and we can also give the package itself an alias.

The following box::use declaration illustrates these different cases:

box::use(
    purrr,                          # 1
    tbl = tibble,                   # 2
    dplyr = dplyr[filter, select],  # 3
    stats[st_filter = filter, ...]  # 4
)

Users of Python, JavaScript, Rust and many other programming languages will find this use declaration familiar (even if the syntax differs):

The code

  1. imports the package ‘purrr’ (but does not attach any of its names);
  2. creates an alias tbl for the imported ‘tibble’ package (but does not attach any of its names);
  3. imports the package ‘dplyr’ and additionally attaches the names dplyr::filter and dplyr::select; and
  4. attaches all exported names from ‘stats’, but uses the local alias st_filter for the name stats::filter.

Of the four packages loaded in the code above, only ‘purrr’, ‘tibble’ and ‘dplyr’ are made available by name (as purrr, tbl and dplyr, respectively), and we can use their exports via the $ operator, e.g. purrr$map or tbl$glimpse. Although we’ve also loaded ‘stats’, we did not create a local name for the package itself, we only attached its exported names.

Thanks to aliases, we can safely use functions with the same name from multiple packages without conflict: in the above, st_filter refers to the filter function from the ‘stats’ package; by contrast, plain filter refers to the ‘dplyr’ function. Alternatively, we could also explicitly qualify the package alias, and write dplyr$filter.

Furthermore, unlike with library, the effects of box::use are restricted to the current scope: we can load and attach names inside a function, and this will not affect the calling scope (or elsewhere). So importing code happens locally, and functions which load packages no longer cause global side effects:

log = function (msg) {
    box::use(glue[glue])
    message(glue('[LOG MESSAGE] {msg}'))
}

log('test')
#> [LOG MESSAGE] test

# … 'glue' is still undefined at this point!

This makes it easy to encapsulate code with external dependencies without creating unintentional, far-reaching side effects.

‘box’ itself is never loaded via library. Instead, its functionality is always used explicitly via box::use.

Why ‘box’?

‘box’ promotes the opposite philosophy of what’s common in R: some notable packages export and attach many hundreds and, in at least one notable case, over a thousand names. This works adequately for small-ish analysis scripts but breaks down for even moderately large software projects because it makes it non-obvious where names are imported from, and increases the risk of name clashes.

To make code more explicit, readable and maintainable, software engineering best practices encourage limiting both the scope of names, as well as the number of names available in each scope.

For instance, best practice in Python is to never use the equivalent of library(pkg) (i.e. from pkg import *). Instead, Python strongly encourages using import pkg or from pkg import a, few, symbols, which correspond to box::use(pkg) and box::use(pkg[a, few, symbols]), respectively. The same is true in many other languages, e.g. C++, Rust and Perl. Other languages (e.g. JavaScript and Go) are even stricter: they don’t allow unqualified imports at all.

The Zen of Python puts this rule succinctly:

Explicit is better than implicit.

‘box’ also makes it drastically easier to write reusable code: instead of needing to create a package, each R code file is already a module which can be imported using box::use. Modules can also be nested inside directories, such that self-contained projects can be easily split into separate or interdependent submodules.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].