rlang (version 0.1)

quosure: Create quosures

Description

Quosures are quoted expressions that keep track of an environment (just like closurefunctions). They are implemented as a subclass of one-sided formulas. They are an essential piece of the tidy evaluation framework.

  • quo() quotes its input (i.e. captures R code without evaluation), captures the current environment, and bundles them in a quosure.

  • enquo() takes a symbol referring to a function argument, quotes the R code that was supplied to this argument, captures the environment where the function was called (and thus where the R code was typed), and bundles them in a quosure.

  • quos() is a bit different to other functions as it returns a list of quosures. You can supply several expressions directly, e.g. quos(foo, bar), but more importantly you can also supply dots: quos(...). In the latter case, expressions forwarded through dots are captured and transformed to quosures. The environments bundled in those quosures are the ones where the code was supplied as arguments, even if the dots were forwarded multiple times across several function calls.

  • new_quosure() is the only constructor that takes its arguments by value. It lets you create a quosure from an expression and an environment.

Usage

quo(expr)

new_quosure(expr, env = caller_env())

enquo(arg)

Arguments

expr

An expression.

env

An environment specifying the lexical enclosure of the quosure.

arg

A symbol referring to an argument. The expression supplied to that argument will be captured unevaluated.

Value

A formula whose right-hand side contains the quoted expression supplied as argument.

Role of quosures for tidy evaluation

Quosures play an essential role thanks to these features:

  • They allow consistent scoping of quoted expressions by recording an expression along with its local environment.

  • quo(), quos() and enquo() all support quasiquotation. By unquoting other quosures, you can safely combine expressions even when they come from different contexts. You can also unquote values and raw expressions depending on your needs.

  • Unlike formulas, quosures self-evaluate (see eval_tidy()) within their own environment, which is why you can unquote a quosure inside another quosure and evaluate it like you've unquoted a raw expression.

See the programming withdplyr vignette for practical examples. For developers, the tidyevaluation vignette provides an overview of this approach. The quasiquotation page goes in detail over the unquoting and splicing operators.

See Also

expr() for quoting a raw expression with quasiquotation. The quasiquotation page goes over unquoting and splicing.

Examples

Run this code
# NOT RUN {
# quo() is a quotation function just like expr() and quote():
expr(mean(1:10 * 2))
quo(mean(1:10 * 2))

# It supports quasiquotation and allows unquoting (evaluating
# immediately) part of the quoted expression:
quo(mean(!! 1:10 * 2))

# What makes quo() often safer to use than quote() and expr() is
# that it keeps track of the contextual environment. This is
# especially important if you're referring to local variables in
# the expression:
var <- "foo"
quo <- quo(var)
quo

# Here `quo` quotes `var`. Let's check that it also captures the
# environment where that symbol is defined:
identical(get_env(quo), get_env())
env_has(quo, "var")


# Keeping track of the environment is important when you quote an
# expression in a context (that is, a particular function frame)
# and pass it around to other functions (which will be run in their
# own evaluation frame):
fn <- function() {
  foobar <- 10
  quo(foobar * 2)
}
quo <- fn()
quo

# `foobar` is not defined here but was defined in `fn()`'s
# evaluation frame. However, the quosure keeps track of that frame
# and is safe to evaluate:
eval_tidy(quo)


# Like other formulas, quosures are normally self-quoting under
# evaluation:
eval(~var)
eval(quo(var))

# But eval_tidy() evaluates expressions in a special environment
# (called the overscope) where they become promises. They
# self-evaluate under evaluation:
eval_tidy(~var)
eval_tidy(quo(var))

# Note that it's perfectly fine to unquote quosures within
# quosures, as long as you evaluate with eval_tidy():
quo <- quo(letters)
quo <- quo(toupper(!! quo))
quo
eval_tidy(quo)


# Quoting as a quosure is necessary to preserve scope information
# and make sure objects are looked up in the right place. However,
# there are situations where it can get in the way. This is the
# case when you deal with non-tidy NSE functions that do not
# understand formulas. You can inline the RHS of a formula in a
# call thanks to the UQE() operator:
nse_function <- function(arg) substitute(arg)
var <- locally(quo(foo(bar)))
quo(nse_function(UQ(var)))
quo(nse_function(UQE(var)))

# This is equivalent to unquoting and taking the RHS:
quo(nse_function(!! get_expr(var)))

# One of the most important old-style NSE function is the dollar
# operator. You need to use UQE() for subsetting with dollar:
var <- quo(cyl)
quo(mtcars$UQE(var))

# `!!`() is also treated as a shortcut. It is meant for situations
# where the bang operator would not parse, such as subsetting with
# $. Since that's its main purpose, we've made it a shortcut for
# UQE() rather than UQ():
var <- quo(cyl)
quo(mtcars$`!!`(var))


# When a quosure is printed in the console, the brackets indicate
# if the enclosure is the global environment or a local one:
locally(quo(foo))

# Literals are enquosed with the empty environment because they can
# be evaluated anywhere. The brackets indicate "empty":
quo(10L)

# To differentiate local environments, use str(). It prints the
# machine address of the environment:
quo1 <- locally(quo(foo))
quo2 <- locally(quo(foo))
quo1; quo2
str(quo1); str(quo2)

# You can also see this address by printing the environment at the
# console:
get_env(quo1)
get_env(quo2)


# new_quosure() takes by value an expression that is already quoted:
expr <- quote(mtcars)
env <- as_env("datasets")
quo <- new_quosure(expr, env)
quo
eval_tidy(quo)
# }

Run the code above in your browser using DataCamp Workspace