R: Evaluate an expression in an environment

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()`:

- `return()` calls are evaluated in frame environments that might be burried deep in the call stack. This causes a long return that unwinds multiple frames (triggering the `on.exit()` event for each frame). By contrast `eval()` only returns from the `eval()` call, one level up.

- `on.exit()`, `parent.frame()`, `sys.call()`, and generally all the stack inspection functions `sys.xxx()` are evaluated in the correct frame environment. This is similar to how this type of calls can be evaluated deep in the call stack because of lazy evaluation, when you force an argument that has been passed around several times.

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 0.4.2 Index]