R: Evaluate an expression in an environment

eval_bare {rlang} | R Documentation

## Evaluate an expression in an environment

### Description

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

### Usage

    eval_bare(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 `ctxt_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 `base::return()`, `base::parent.frame()` may return misleading results.

### See Also

with_env

### Examples

    # eval_bare() works just like base::eval():
    env <- child_env(NULL, foo = "bar")
    expr <- quote(foo)
    eval_bare(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 = get_env()
      )
    }
    middle <- function(eval_fn) {
      deep(eval_fn, get_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)

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

[Package rlang version 0.1.6 Index]