box icon indicating copy to clipboard operation
box copied to clipboard

Writing a non-package using box, and then converting to a package

Open multimeric opened this issue 3 years ago • 2 comments

Hi,

I like the look of box, but one concern I have is that the documentation doesn't really talk about its use within packages. I'm wondering if the box::use statements will work if inside a published package, considering that (from my limited understanding of packages), the entire script is never actually executed for a package, but rather single functions are exported in isolation. If this is true, then it feels like box has a significant downside over a regular script, in that it makes migrating ad-hoc code into a package even harder. It's possible to write an ad-hoc script that doesn't use library/require at all, only ever uses ::, and @exports functions where relevant, and then it is then quite trivial to migrate this into a package. But if box doesn't work with modules, then there is no simple way to write code in a module-compatible way. Migration into a package will involve removing all the box::use calls, and replacing module$function with module::function, which is not trivial via find and replace.

Do the authors have any thoughts on how to solve this problem? I want to use box to replace the use of source, because I'm not a fan of the way it works, but I don't want to also lose the easy migration to a package for all my code.

multimeric avatar Jul 01 '21 14:07 multimeric

On a philosophical level, the package author (i.e., me) sees modules as a successor and replacement of packages. So, rather than migrating code from modules into packages, code should be published as modules. But of course that’s my optimistic view of the future, and is currently not yet realistic, given the much more advanced infrastructure support for packages at every level of R.

That said, ‘box’ modules can be included and used inside packages (non-included, external modules can also be used, of course). Inclusion of modules into packages is however somewhat tricky, and part of the reason is that so far nobody yet requested better support for it. Which I might now reconsider.

To include modules into a package, they need to be placed into the inst directory of the package source. Then1, at package load time, the module script path needs to be set explicitly to the package directory. And of course ‘box’ needs to be added as a package import dependency.

Here’s a minimal example:

R/hello.r

.onLoad = function (libname, pkgname) {
    box::set_script_path(file.path(system.file(package = pkgname), '_'))
}

#' @export
hello_world = function (name) {
    box::use(./hello)
    hello$world(name)
}

inst/hello.r

#' @export
world = function (name) {
    box::use(glue)
    message(glue$glue('Hello, {name}'))
}

The one caveat is (currently) that, since the script path must be set at package load time2, modules cannot be loaded at file level, only inside functions (because file-level code in packages is executed at build time, not at load time).


1 because of the current lack of explicit support in ‘box’ to do this automatically.

2 the reason is explained by Tomas Kalibera

klmr avatar Jul 01 '21 22:07 klmr

Thanks, that seems like a reasonable solution.

multimeric avatar Jul 02 '21 02:07 multimeric