eval_bare {rlang}R Documentation

Evaluate an expression in an environment

Description

eval_bare() is a lower-level version of function base::eval(). Technically, it is a simple wrapper around the C function Rf_eval(). You generally don't need to use eval_bare() instead of eval(). Its main advantage is that it handles stack-sensitive calls (such as return(), on.exit() or parent.frame()) more consistently when you pass an enviroment of a frame on the call stack.

Usage

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

Arguments

expr

An expression to evaluate.

env

The environment in which to evaluate the expression.

Details

These semantics are possible because eval_bare() creates only one frame on the call stack whereas eval() creates two frames, the second of which has the user-supplied environment as frame environment. When you supply an existing frame environment to base::eval() there will be two frames on the stack with the same frame environment. Stack-sensitive functions only detect the topmost of these frames. We call these evaluation semantics "stack inconsistent".

Evaluating expressions in the actual frame environment has useful practical implications for eval_bare():

The flip side of the semantics of eval_bare() is that it can't evaluate break or next expressions even if called within a loop.

See Also

eval_tidy() for evaluation with data mask and quosure support.

Examples

# eval_bare() works just like base::eval() but you have to create
# the evaluation environment yourself:
eval_bare(quote(foo), env(foo = "bar"))

# eval() has different evaluation semantics than eval_bare(). It
# can return from the supplied environment even if its an
# environment that is not on the call stack (i.e. because you've
# created it yourself). The following would trigger an error with
# eval_bare():
ret <- quote(return("foo"))
eval(ret, env())
# eval_bare(ret, env())  # "no function to return from" error

# Another feature of eval() is that you can control surround loops:
bail <- quote(break)
while (TRUE) {
  eval(bail)
  # eval_bare(bail)  # "no loop for break/next" error
}

# 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 with 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 = current_env()
  )
}
middle <- function(eval_fn) {
  deep(eval_fn, current_env())
}
deep <- function(eval_fn, eval_env) {
  expr <- quote(parent.frame())
  eval_fn(expr, eval_env)
}

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

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

[Package rlang version 1.1.3 Index]