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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With