Reputation: 20329
I want to define a function in an environment, which uses globals from this environment:
e <- new.env()
e$a <- 1
e$f <- function() a
## e$f()
## Error in e$f() : object 'a' not found
environment(e$f) <- e
e$f()
# [1] 1
So far so good. Now assume I add another (locked) binding to e
:
e$b <- 2
lockBinding("b", e)
How could I (temporarily) unlock this binding from within f
without using the symbol e
?
e$f <- function() unlockBinding("b", parent.frame())
environment(e$f) <- e
e$f()
does not work because parent.frame
does not properly point to e
.
I do not want to use e
directly because this would break as soon as I clone environments:
e <- new.env()
e$a <- e$b <- 1
lockBinding("b", e)
e$f <- function() unlockBinding("b", e)
environment(e$f) <- e
## e$b <- 3
## Error in e$b <- 3 : cannot change value of locked binding for 'b'
g <- rlang::env_clone(e)
g$f()
e$b <- 3 ## should not work
Upvotes: 2
Views: 124
Reputation: 269556
The proto package facilitates this sort of thing avoiding most explicit mucking with environments. A proto object is an environment with the class proto that has its own methods. See the package vignette for more information.
library(proto)
# create proto object containing 3 objects
p <- proto(a = 1, b = 1, f = function(.) unlockBinding("b", .))
p$lockBinding(sym = "b") # lock b
p$b <- 3 # error since p$b is locked
p2 <- as.proto(p$as.list()) # clone p
p2$b <- 4 # ok since p2 is distinct from p
p$b <- 3 # as expected, this is an error since p$b is locked
ch <- p$proto() # create a child of p
ch$b # b is not in ch so it finds it in parent p
## [1] 1
ch$b <- 10 # ok since this creates a new b in ch overriding p$b
p$f() # unlock b
p$b <- 20 # ok
Upvotes: 3
Reputation: 20329
Ok, found the solution:
e$f <- function() unlockBinding("b", parent.env(environment()))
should do the trick.
Upvotes: 2