Become an expert in R — Interactive courses, Cheat Sheets, certificates and more!
Get Started for Free

eval_tidy

Evaluate an expression with quosures and pronoun support


Description

Stable lifecycle

eval_tidy() is a variant of base::eval() that powers the tidy evaluation framework. Like eval() it accepts user data as argument. Whereas eval() simply transforms the data to an environment, eval_tidy() transforms it to a data mask with as_data_mask(). Evaluating in a data mask enables the following features:

  • Quosures. Quosures are expressions bundled with an environment. If data is supplied, objects in the data mask always have precedence over the quosure environment, i.e. the data masks the environment.

  • Pronouns. If data is supplied, the .env and .data pronouns are installed in the data mask. .env is a reference to the calling environment and .data refers to the data argument. These pronouns lets you be explicit about where to find values and throw errors if you try to access non-existent values.

Usage

eval_tidy(expr, data = NULL, env = caller_env())

Arguments

expr

An expression or quosure to evaluate.

data

A data frame, or named list or vector. Alternatively, a data mask created with as_data_mask() or new_data_mask(). Objects in data have priority over those in env. See the section about data masking.

env

The environment in which to evaluate expr. This environment is not applicable for quosures because they have their own environments.

Data masking

Data masking refers to how columns or objects inside data have priority over objects defined in env (or in the quosure environment, if applicable). If there is a column var in data and an object var in env, and expr refers to var, the column has priority:

var <- "this one?"
data <- data.frame(var = rep("Or that one?", 3))

within <- function(data, expr) {
  eval_tidy(enquo(expr), data)
}

within(data, toupper(var))
#> [1] "OR THAT ONE?" "OR THAT ONE?" "OR THAT ONE?"

Because the columns or objects in data are always found first, before objects from env, we say that the data "masks" the environment.

When should eval_tidy() be used instead of eval()?

base::eval() is sufficient for simple evaluation. Use eval_tidy() when you'd like to support expressions referring to the .data pronoun, or when you need to support quosures.

If you're evaluating an expression captured with quasiquotation support, it is recommended to use eval_tidy() because users will likely unquote quosures.

Note that unwrapping a quosure with quo_get_expr() does not guarantee that there is no quosures inside the expression. Quosures might be unquoted anywhere. For instance, the following does not work reliably in the presence of nested quosures:

my_quoting_fn <- function(x) {
  x <- enquo(x)
  expr <- quo_get_expr(x)
  env <- quo_get_env(x)
  eval(expr, env)
}

# Works:
my_quoting_fn(toupper(letters))

# Fails because of a nested quosure:
my_quoting_fn(toupper(!!quo(letters)))

Stack semantics of eval_tidy()

eval_tidy() always evaluates in a data mask, even when data is NULL. Because of this, it has different stack semantics than base::eval():

  • Lexical side effects, such as assignment with <-, occur in the mask rather than env.

  • Functions that require the evaluation environment to correspond to a frame on the call stack do not work. This is why return() called from a quosure does not work.

  • The mask environment creates a new branch in the tree representation of backtraces (which you can visualise in a browser() session with lobstr::cst()).

See also eval_bare() for more information about these differences.

Life cycle

rlang 0.3.0

Passing an environment to data is deprecated. Please construct an rlang data mask with new_data_mask().

See Also

nse-force for the second leg of the tidy evaluation framework.

Examples

# With simple quoted expressions eval_tidy() works the same way as
# eval():
apple <- "apple"
kiwi <- "kiwi"
expr <- quote(paste(apple, kiwi))
expr

eval(expr)
eval_tidy(expr)

# Both accept a data mask as argument:
data <- list(apple = "CARROT", kiwi = "TOMATO")
eval(expr, data)
eval_tidy(expr, data)


# In addition eval_tidy() has support for quosures:
with_data <- function(data, expr) {
  quo <- enquo(expr)
  eval_tidy(quo, data)
}
with_data(NULL, apple)
with_data(data, apple)
with_data(data, list(apple, kiwi))

# Secondly eval_tidy() installs handy pronouns that allow users to
# be explicit about where to find symbols:
with_data(data, .data$apple)
with_data(data, .env$apple)


# Note that instead of using `.env` it is often equivalent and may
# be preferred to unquote a value. There are two differences. First
# unquoting happens earlier, when the quosure is created. Secondly,
# subsetting `.env` with the `$` operator may be brittle because
# `$` does not look through the parents of the environment.
#
# For instance using `.env$name` in a magrittr pipeline is an
# instance where this poses problem, because the magrittr pipe
# currently (as of v1.5.0) evaluates its operands in a *child* of
# the current environment (this child environment is where it
# defines the pronoun `.`).
## Not run: 
  data %>% with_data(!!kiwi)     # "kiwi"
  data %>% with_data(.env$kiwi)  # NULL

## End(Not run)

rlang

Functions for Base Types and Core R and 'Tidyverse' Features

v0.4.11
MIT + file LICENSE
Authors
Lionel Henry [aut, cre], Hadley Wickham [aut], mikefc [cph] (Hash implementation based on Mike's xxhashlite), Yann Collet [cph] (Author of the embedded xxHash library), RStudio [cph]
Initial release

We don't support your browser anymore

Please choose more modern alternatives, such as Google Chrome or Mozilla Firefox.