Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

match.call called in wrong environment when eval’ing

I tried implementing a function let with the following semantics:

> let(x = 1, y = 2, x + y)
[1] 3

… which is conceptually somewhat similar to substitute with the syntax of with.

The following code almost works (the above invocation for instance works):

let <- function (...) {
    args <- match.call(expand.dots = FALSE)$`...`
    expr <- args[[length(args)]]
    eval(expr,
         list2env(lapply(args[-length(args)], eval), parent = parent.frame()))
}

Note the nested eval, the outer to evaluate the actual expression and the inner to evaluate the arguments.

Unfortunately, the latter evaluation happens in the wrong context. This becomes apparent when trying to call let with a function that examines the current frame, such as match.call:

> (function () let(x = match.call(), x))()
Error in match.call() :
  unable to find a closure from within which 'match.call' was called

I thought of supplying the parent frame as the evaluating environment for eval, but that doesn’t work:

let <- function (...) {
    args <- match.call(expand.dots = FALSE)$`...`
    expr <- args[[length(args)]]
    parent <- parent.frame()
    eval(expr,
         list2env(lapply(args[-length(args)], function(x) eval(x, parent)),
                  parent = parent)
}

This yields the same error. Which leads me to the question: how exactly is match.call evaluated? Why doesn’t this work? And, how do I make this work?

like image 1000
Konrad Rudolph Avatar asked May 01 '13 13:05

Konrad Rudolph


1 Answers

Will this rewrite solve your problem?

let <- function (expr, ...) {
    expr  <- match.call(expand.dots = FALSE)$expr
    given <- list(...)
    eval(expr, list2env(given, parent = parent.frame()))
}

let(x = 1, y = 2, x + y)
# [1] 3
like image 129
flodel Avatar answered Nov 15 '22 21:11

flodel