thothal
thothal

Reputation: 20329

Get environment of function from within the function

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

Answers (2)

G. Grothendieck
G. Grothendieck

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

thothal
thothal

Reputation: 20329

Ok, found the solution:

e$f <- function() unlockBinding("b", parent.env(environment()))

should do the trick.

Upvotes: 2

Related Questions