rlang (version 0.0.0.9000)

expr_eval: Evaluate an expression in an environment.

Description

expr_eval() is a lightweight version of the base function eval(). It does not accept supplementary data, but it is more efficient and does not clutter the evaluation stack. Technically, expr_eval() is a simple wrapper around the C function Rf_eval().

Usage

expr_eval(expr, env = parent.frame())

Arguments

expr
An expression to evaluate.
env
The environment in which to evaluate the expression.

Details

base::eval() inserts two call frames in the stack, the second of which features the envir parameter as frame environment. This may unnecessarily clutter the evaluation stack and it can change evaluation semantics with stack sensitive functions in the case where env is an evaluation environment of a stack frame (see eval_stack()). Since the base function eval() creates a new evaluation context with env as frame environment there are actually two contexts with the same evaluation environment on the stack when expr is evaluated. Thus, any command that looks up frames on the stack (stack sensitive functions) may find the parasite frame set up by eval() rather than the original frame targetted by env. As a result, code evaluated with base::eval() does not have the property of stack consistency, and stack sensitive functions like return(), parent.frame() may return misleading results.

See Also

with_env

Examples

Run this code
# expr_eval() works just like base::eval():
env <- new_env(data = list(foo = "bar"))
expr <- quote(foo)
expr_eval(expr, env)

# To explore the consequences of stack inconsistent semantics, let's
# create a function that evaluates `parent.frame()` deep in the call
# stack, in an environment corresponding to a frame in the middle of
# the stack. For consistency we R's lazy evaluation semantics, we'd
# expect to get the caller of that frame as result:
fn <- function(eval_fn) {
  list(
    returned_env = middle(eval_fn),
    actual_env = env()
  )
}
middle <- function(eval_fn) {
  deep(eval_fn, env())
}
deep <- function(eval_fn, eval_env) {
  expr <- quote(parent.frame())
  eval_fn(expr, eval_env)
}

# With expr_eval(), we do get the expected environment:
fn(rlang::expr_eval)

# But that's not the case with base::eval():
fn(base::eval)

# Another difference of expr_eval() compared to base::eval() is
# that it does not insert parasite frames in the evaluation stack:
get_stack <- quote(identity(eval_stack()))
expr_eval(get_stack)
eval(get_stack)

Run the code above in your browser using DataLab