match.call called in wrong environment when eval’ing

eval, r, substitution

Solution

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

Problem

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?

Original source