6

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.

landau
  • 5,636
  • 1
  • 22
  • 50
  • Can you pre-create it? For instance, `rnorm(1); lockEnvironment(globalenv()); rnorm(10)` works just fine. Realize that `lockEnvironment` is preventing new variables, it does not prevent changing an existing variable. – r2evans Feb 21 '19 at 16:56
  • Related to a previous question (https://stackoverflow.com/q/19132492/3358272), it would seem that this new default mode of `drake::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 of `drake` is to enforce reproducibility, so this is not an undesirable thing, but it is irreversible. – r2evans Feb 21 '19 at 16:59
  • Responses: (1) Yes, `drake` 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 custom [`unlock_environment()`](https://github.com/ropensci/drake/blob/master/src/unlock_environment.c) function. – landau Feb 21 '19 at 17:21
  • I can't find anything that would support `mostlyLockEnvironment()`. (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?) – r2evans Feb 21 '19 at 18:22
  • I am surprised too! But apparently, `parallel::mclapply()` and `shiny` both try to remove it. See [here](https://stackoverflow.com/questions/54229295/parallelmclapply-adds-or-removes-bindings-to-the-global-environment-which-o), [here](https://github.com/ropensci/drake/issues/675), and [here](https://github.com/ropensci/drake/issues/749). – landau Feb 21 '19 at 19:08
  • 1
    Interesting! Thanks for the links. Frankly, whenever something imposes side-effect like that, I tend to get nervous/annoyed. I suppose it's for a good reason, somehow, I'll have to press the `[I believe]` button for now ... – r2evans Feb 21 '19 at 19:21

0 Answers0