1

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.

SimonG
  • 4,701
  • 3
  • 20
  • 31
  • I don't understand your basic premise. Why can't you simply `trace` or `fixInNamespace` `foo`? – Roland Feb 02 '16 at 14:01
  • @Roland: I haven't worked with `trace` or `fixInNamespace` before, but it looks like they would require me to redefine `foo` completely. In fact, `foo` is part of a large and actively developed package, and I would prefer `foo` to remain unchanged (probably easier to maintain, too). – SimonG Feb 02 '16 at 14:11
  • No, both allow you to insert code into the function. Maybe you should consider contacting the maintainer regarding changing `foo` for your use case. – Roland Feb 02 '16 at 14:13
  • @Roland: Yes, contacting the maintainer would be best. Here I was looking for the "hacky" solution. Inserting the code may do it, but *where* the code must be inserted might change with future changes in `foo`. If you could provide an example of your proposal as an answer, that'd be a great help. – SimonG Feb 02 '16 at 14:23
  • http://stackoverflow.com/q/2458013/1412059 – Roland Feb 02 '16 at 14:26

0 Answers0