Question

In my R development I need to wrap function primitives in proto objects so that a number of arguments can be automatically passed to the functions when the $perform() method of the object is invoked. The function invocation internally happens via do.call(). All is well, except when the function attempts to access variables from the closure within which it is defined. In that case, the function cannot resolve the names.

Here is the smallest example I have found that reproduces the behavior:

library(proto)

make_command <- function(operation) {
  proto(
    func = operation,
    perform = function(., ...) {
      func <- with(., func) # unbinds proto method
      do.call(func, list(), envir=environment(operation))
    }  
    )
}

test_case <- function() {
  result <- 100  
  make_command(function() result)$perform()
}

# Will generate error:
# Error in function ()  : object 'result' not found
test_case()

I have a reproducible testthat test that also outputs a lot of diagnostic output. The diagnostic output has me stumped. By looking up the parent environment chain, my diagnostic code, which lives inside the function, finds and prints the very same variable the function fails to find. See this gist..

How can the environment for do.call be set up correctly?

Was it helpful?

Solution

This was the final answer after an offline discussion with the poster:

make_command <- function(operation) {
 proto(perform = function(.) operation())
}

OTHER TIPS

I think the issue here is clearer and easier to explore if you:

  • Replace the anonymous function within make_command() with a named one.

  • Make that function open a browser() (instead of trying to get result). That way you can look around to see where you are and what's going on.

Try this, which should clarify the cause of your problem:

test_case <- function() {
  result <- 100  
  myFun <- function() browser()
  make_command(myFun)$perform()
}
test_case()
## Then from within the browser:
#
parent.env(environment())
# <environment: 0x0d8de854>
# attr(,"class")
# [1] "proto"       "environment"
get("result", parent.env(environment()))
# Error in get("result", parent.env(environment())) : 
#   object 'result' not found
#
parent.frame()
# <environment: 0x0d8ddfc0>
get("result", parent.frame())  ## (This works, so points towards a solution.)
# [1] 100

Here's the problem. Although you think you're evaluating myFun(), whose environment is the evaluation frame of test_case(), your call to do.call(func, ...) is really evaluating func(), whose environment is the proto environment within which it was defined. After looking for and not finding result in its own frame, the call to func() follows the rules of lexical scoping, and next looks in the proto environment. Neither it nor its parent environment contains an object named result, resulting in the error message you received.

If this doesn't immediately make sense, you can keep poking around within the browser. Here are a few further calls you might find helpful:

environment(get("myFun", parent.frame()))
ls(environment(get("myFun", parent.frame())))
environment(get("func", parent.env(environment())))
ls(environment(get("func", parent.env(environment()))))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top