3

I wrote a function that uses match.call, and it works when I call the function directly, however, when the function is called within another function it breaks. I believe it has to do with how match.call handles environments, but I can't figure it out. Here is a reproducable example:

tester <- function() {

  var <- "helloworld"

  myFunc(var)

}


myFunc <- function(x) {

  tmp <- match.call()
  tmp[[1]] <- quote(toupper)
  eval(tmp)

}

tester() # error

myFunc("helloworld") # works fine

I believe that when myFunc is called within tester it can't find var because it exists in the isolated environment of the tester function.

Any ideas on how to get myFunc to work inside tester would be appreciated. I've tried changing the environment for eval and match.call, but to no avail.

Carl
  • 5,569
  • 6
  • 39
  • 74

1 Answers1

5

Your suspicion is exactly right.

The very simple solution is to evaluate the function in its parent’s context: replace eval with eval.parent.

myFunc <- function(x) {
  tmp <- match.call()
  tmp[[1]] <- quote(toupper)
  eval.parent(tmp)
}

Generally, the function traceback can help tremendously with debugging similar issues:

> tester()
Error in as.character(x) :
  cannot coerce type 'closure' to vector of type 'character'

Ok, but which “closure” (= function) are we talking about?

> traceback()
5: toupper(x = var)
4: eval(expr, envir, enclos)
3: eval(tmp) at #5
2: myFunc(var) at #5
1: tester()

The first line has the clue: toupper(x = var). In the context of tester, var refers to the function (closure) stats::var which is found because its package is attached.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Oh, and changing the environment of `eval` works as well. In fact, that’s exactly what `eval.parent` does; it’s equivalent to `eval(tmp, envir = parent.frame())`. Don’t be misled by the fact that this is the default value of `eval`’s parameter `envir` anyway: specifying an argument value and using its default can produce different values because parameter defaults are evaluated *inside* the function whereas arguments you pass into a function are evaluated outside of it. This is super confusing. – Konrad Rudolph Oct 21 '16 at 15:59
  • Technically, it's equivalent to `eval(tmp,envir = parent.frame(2))`, which I swore I tried, but this seems to work so thanks – Carl Oct 21 '16 at 16:04
  • Ah, I see your comment – Carl Oct 21 '16 at 16:05