crate() no longer requires .fn to be
defined inside the call. Existing functions can now be passed
directly:
Plain closures have their enclosing environment replaced with the
crate’s evaluation environment, equivalent to the previous workaround
crate(rlang::set_env(fn)).
Namespace functions and primitives are wrapped in a thin closure that delegates to them, which preserves their internal lookups and avoids a deprecation warning when setting attributes on primitives.
Existing crates are passed through unchanged.
As data declared via ... is only reachable from
plain closures, it is an error to supply ... when
.fn is an existing crate, a namespace function, or a
primitive.
The printout of a crate now includes data bound to names starting with a dot.
This release modifies the behaviour of functions crated via
..., i.e. in cases like:
foo <- function(x) x + 1
crate(\(x) foo(x), foo = foo)Functions passed through ..., such as foo
in this example, are now automatically crated in the same
environment as the main crate. This behaviour change may break your code
in some cases but has two important benefits that we thought were worth
it:
In the global environment, it allows helper functions to depend on other helper functions defined globally. Take for instance:
foo <- function(x) bar(x)
bar <- function(x) x + 1
crate(\(x) foo(x), foo = foo, bar = bar)Before this change, both foo and bar would
inherit from the global environment, before and after being sent to a
remote context. While the local context has both foo and
bar defined in the global environment, this would not be
the case remotely, and foo would not be able to resolve
bar.
Now that we recrate all ... closures withing the main
crate environment, they are able to see each other.
In a local environment (e.g. in a function or within
local()), the chain of closure environments used to be
included in the crate up to the global environment. This meant the
problem descibed in (1) didn’t apply in local environments, but the flip
side is that it was also easy to inadvertently capture large objects
defined in these environments:
local({
large_object <- rnorm(1e8)
parameter <- 1
foo <- function(x) x + parameter
crate(\(x) foo(x), foo = foo)
})In this example, large_object would be included in the
crate alongside foo, causing performance issues when
sending the crate to a remote context. This is no longer possible now
that foo is automatically crated. Any relevant object must
be explicitly crated, in one of these ways:
local({
parameter <- 1
foo <- function(x) x + parameter
crate(\(x) foo(x), foo = foo, parameter = parameter)
})
# Or equivalently
local({
parameter <- 1
foo <- crate(\(x) x + parameter, parameter = parameter)
crate(\(x) foo(x), foo = foo)
})crate() gains a .parent_env argument.
The default is baseenv() in order to isolate the crate from
the global search path. You can now set it to another environment. For
instance, set it to globalenv() to make your crate inherit
from the search path. Note that, as the global environment is serialized
by name rather than by value, the crate is still isolated from objects
in the global environment (#16).
crate() now requires all ... arguments
to be named instead of silently dropping unnamed arguments
(#15).
crate() gains .error_arg and
.error_call arguments to allow for better error messages
when calling crate() from other functions (#14).
Crated functions no longer carry source references (#6).
Fixed issue that prevented crate sizes to be printed in human-readable format (r-lib/lobstr#60).
Initial release. The package currently only contains a single method for creating crates that requires users to be explicit about what data to pack in the crate. A future release will provide a method to figure out automatically what objects the crate depends on (with inevitable false positives and negatives).