I am interested in manipulating the content of the ellipsis (...
) in the parent frame of a function. In other words, I want to write a function that is able to manipulate the environment from which it is called. How can I do that (even though I know I shouldn't)?
Example:
I have an upper-level function foo
which acts as a wrapper around a lower-level function bar
. Among other tasks, it calls bar
repeatedly. The function foo
takes two arguments; one named x
, the other named y
, where y
is part of the ellipsis. As a minimal example, imagine foo
looks as follows.
foo <- function(x,...){
out <- numeric(5)
for(i in 1:5) out[i] <- bar(x,...)
return(out)
}
Note: I cannot change the content of foo
because it's part of a large R package; I cannot, for example, unpack the ellipsis into something like bar.args <- list(...)
and manipulate it within foo
. This makes it tricky.
Inside foo
, the lower-level function bar
takes care of the actual computations. In addition (though not its sole purpose), it should be able to manipulate the content of the ellipsis in its parent frame, that is, it should receive limited control over how it is run when it is called multiple times. As a minimal example, bar
might increase the value of y
after each time it is called.
bar <- function(...){
ans <- x + list(...)$y
# change in parent frame: ...$y <- ...$y + 1
return(ans)
}
Since the arguments of bar
are contained in the ellipsis, I'm not aware of any nice and (relatively) safe way to do this. My own idea was to create a copy of y
in the parent frame that I can easily access and modify.
bar <- function(x,...){
# check
chk <- mget("copyof.y", envir=parent.frame(), ifnotfound=NA)
# change y or a copy of it
if(is.na(chk$copyof.y)){
ans <- x + list(...)$y
assign("copyof.y", list(...)$y+1, pos=parent.frame())
}else{
ans <- x + chk$copyof.y
assign("copyof.y", chk$copyof.y+1, pos=parent.frame())
}
return(ans)
}
# > foo(1,y=1)
# [1] 2 3 4 5 6
I realize, that this is "bad practice" in R because the closure of the function is being broken, thus pretty much nullifying what functions are usually for... However, I am not in control over foo
and I can only change what bar
is doing. In other words, I am trying to trick foo
into allowing that bar
has limited control over how it is run.