R’s eval vs get

I’ve been fighting damn scoping issues lately and finally sat down to figure out what works and when. Documenting some of them here

The lessons I’ve learned from this:

  1. eval(a, envir = … ) DOES NOT WORK!
  2. Instead, do eval(str2lang(“a”), envir = …)
  3. eval.parent(a) DOES NOT WORK!
  4. eval.parent(str2lang(“a”)) works and SKIPS GENERIC!
  5. get(“a”, envir = …) seems to always work as expected

So to summarize, I think get is always a safer option than eval. In addition, get is much faster:

f1 <- function() {
  eval.parent(str2lang("a"))
}
f2 <- function() {
  get("a", envir = sys.frame(0))
}
a <- 1
microbenchmark:::microbenchmark(f1(), f2(), times = 10000)
## Unit: nanoseconds
##  expr  min   lq      mean median   uq     max neval
##  f1() 1189 1271 1533.3836   1312 1353 1023401 10000
##  f2()  492  574  683.9497    574  615  499544 10000

Obviously this doesn’t cover more advanced situations, like evaluating full calls, but I’m still working on puzzling all that out.

Here’s the sample code I used to evaluate this:

foo <- function(x) {
  cat("In foo\n")
  a <- "in generic"
  UseMethod("foo")
}

foo.numeric <- function(x) {
  cat("In foo.numeric\n")
  a <- "in specific"

  # Itself
  print(paste("a:",
               a
              ))
  # Eval
  print(paste("eval(a):",
               eval(a)
        ))
  print(paste('eval(str2lang("a")):',
               eval(str2lang("a"))
        ))

  # eval.parent
  print(paste("eval.parent(a):",
               eval.parent(a)
              ))
  print(paste('eval.parent(str2lang("a")):',
               eval.parent(str2lang("a"))
              ))

  # eval with sys.frames
  print(paste("eval(a, envir = sys.frame(0)):",
               eval(a, envir = sys.frame(0))
              ))
  print(paste('eval(str2lang("a"), envir = sys.frame(0)):',
               eval(str2lang("a"), envir = sys.frame(0))
              ))
  print(paste("eval(a, envir = sys.frame(1)):",
               eval(a, envir = sys.frame(1))
              ))
  print(paste('eval(str2lang("a"), envir = sys.frame(1)):',
               eval(str2lang("a"), envir = sys.frame(1))
              ))

  # get
  print(paste('get("a"):',
               get("a")
              ))

  # get with sys.frames
  print(paste('get("a", envir = sys.frame(0)):',
               get("a", envir = sys.frame(0))
              ))
  print(paste('get("a", envir = sys.frame(1)):',
               get("a", envir = sys.frame(1))
              ))
}

a <- "in global"
foo(1)
## In foo
## In foo.numeric
## [1] "a: in specific"
## [1] "eval(a): in specific"
## [1] "eval(str2lang(\"a\")): in specific"
## [1] "eval.parent(a): in specific"
## [1] "eval.parent(str2lang(\"a\")): in global"
## [1] "eval(a, envir = sys.frame(0)): in specific"
## [1] "eval(str2lang(\"a\"), envir = sys.frame(0)): in global"
## [1] "eval(a, envir = sys.frame(1)): in specific"
## [1] "eval(str2lang(\"a\"), envir = sys.frame(1)): in global"
## [1] "get(\"a\"): in specific"
## [1] "get(\"a\", envir = sys.frame(0)): in global"
## [1] "get(\"a\", envir = sys.frame(1)): in global"
Home | Back to blog

This work is licensed under CC BY-NC 4.0 Creative Commons BY-NC image