Is it possible to lock the global environment and still allow .Random.seed
to be set or removed? The default behavior of lockEnvironment()
is too aggressive
for my use case.
lockEnvironment(globalenv())
rnorm(10)
#> Error in rnorm(10) : cannot add bindings to a locked environment
rm(.Random.seed)
#> Error in rm(.Random.seed) :
#> cannot remove bindings from a locked environment
Background
drake
version 7.0.0 will have a new safeguard to protect reproducibility.
plan <- drake_plan(
x = {
data(mtcars)
mtcars$mpg
},
y = mean(x)
)
plan
#> # A tibble: 2 x 2
#> target command
#> <chr> <expr>
#> 1 x { data(mtcars) mtcars$mpg }
#> 2 y mean(x)
make(plan)
#> target x
#> fail x
#> Error: Target `x` failed. Call `diagnose(x)` for details. Error message:
#> cannot add bindings to a locked environment.
#> One of your targets tried to modify your environment,
#> which could invalidate other targets
#> and undermine reproducibility (example:
#> https://github.com/ropensci/drake/issues/664#issuecomment-453163562).
#> Beware <<-, ->>, attach(), data(), and side effects in general.
#> Use make(lock_envir = FALSE) to avoid this error (not recommended).
The error comes from the call to data(mtcars)
. The very act of building x
would have changed x
's dependencies. Without guardrails, the workflow invalidates itself.
make(plan, lock_envir = FALSE)
#> target x
#> target y
make(plan, lock_envir = FALSE)
#> target x
But with guardrails, we run into edge cases like https://github.com/ropensci/drake/issues/749 and https://github.com/ropensci/drake/issues/675#issuecomment-458222414.
rnorm(1); lockEnvironment(globalenv()); rnorm(10)
works just fine. Realize thatlockEnvironment
is preventing new variables, it does not prevent changing an existing variable. – Amplifydrake::make
would itself be a one-way sledge-hammer, since you cannot unlock an environment (global or otherwise) once locked. I recognize that one purpose ofdrake
is to enforce reproducibility, so this is not an undesirable thing, but it is irreversible. – Amplifydrake
already pre-creates the seed. It has to because of how it handles reproducible pseudo-random number generation. But we still have edge cases when user-side code tries to remove.Random.seed
. (2) I deal with that as best I can in a customunlock_environment()
function. – EverrsmostlyLockEnvironment()
. (I'm a bit shocked that user code would attempt to remove that variable ... it's a dot-variable for a reason, I'd think that's a little too over-handed. Do you have a strong example showing utility in removing it?) – Amplifyparallel::mclapply()
andshiny
both try to remove it. See here, here, and here. – Everrs[I believe]
button for now ... – Amplify