quotation {rlang} | R Documentation |
Quotation is a mechanism by which an expression supplied as argument is captured by a function. Instead of seeing the value of the argument, the function sees the recipe (the R code) to make that value. This is possible because R expressions are representable as regular objects in R:
Calls represent the action of calling a function to compute a new value. Evaluating a call causes that value to be computed. Calls typically involve symbols to reference R objects.
Symbols represent the name that is given to an object in a particular context (an environment).
We call objects containing calls and symbols expressions.
There are two ways to create R expressions. First you can build
calls and symbols from parts and pieces (see sym()
, syms()
and
call2()
). The other way is by quotation or quasiquotation,
i.e. by intercepting an expression instead of evaluating it.
expr(expr) enexpr(arg) exprs(..., .named = FALSE, .ignore_empty = c("trailing", "none", "all"), .unquote_names = TRUE) enexprs(..., .named = FALSE, .ignore_empty = c("trailing", "none", "all"), .unquote_names = TRUE) ensym(arg) ensyms(..., .named = FALSE, .ignore_empty = c("trailing", "none", "all"), .unquote_names = TRUE) quo(expr) enquo(arg) quos(..., .named = FALSE, .ignore_empty = c("trailing", "none", "all"), .unquote_names = TRUE) enquos(..., .named = FALSE, .ignore_empty = c("trailing", "none", "all"), .unquote_names = TRUE)
expr |
An expression. |
arg |
A symbol representing an argument. The expression supplied to that argument will be captured instead of being evaluated. |
... |
For |
.named |
Whether to ensure all dots are named. Unnamed
elements are processed with |
.ignore_empty |
Whether to ignore empty arguments. Can be one
of |
.unquote_names |
Whether to treat |
There are two points of view when it comes to capturing an expression:
You can capture the expressions supplied by the user of your
function. This is the purpose of ensym()
, enexpr()
and
enquo()
and their plural variants. These functions take an
argument name and capture the expression that was supplied to
that argument.
You can capture the expressions that you supply. To this end
use expr()
and quo()
and their plural variants exprs()
and
quos()
.
enexpr()
and expr()
capture a single raw expression.
enexprs()
and exprs()
capture a list of raw expressions
including expressions contained in ...
.
ensym()
and ensyms()
are variants of enexpr()
and
enexprs()
that check the captured expression is either a string
(which they convert to symbol) or a symbol. If anything else
is supplied they throw an error.
In terms of base functions, enexpr(arg)
corresponds to
base::substitute(arg)
(though that function has complex
semantics) and expr()
is like quote()
(and bquote()
if we
consider unquotation syntax). The plural variant exprs()
is
equivalent to base::alist()
. Finally there is no function in base
R that is equivalent to enexprs()
but you can reproduce its
behaviour with eval(substitute(alist(...)))
.
quo()
and enquo()
are similar to their expr
counterparts but
capture both the expression and its environment in an object called
a quosure. This wrapper contains a reference to the original
environment in which that expression was captured. Keeping track of
the environments of expressions is important because this is where
functions and objects mentioned in the expression are defined.
Quosures are objects that can be evaluated with eval_tidy()
just
like symbols or function calls. Since they always evaluate in their
original environment, quosures can be seen as a vehicle that allow
expressions to travel from function to function but that beam back
instantly to their original environment upon evaluation.
See the quosure help topic about tools to work with quosures.
All quotation functions in rlang have support for unquoting operators. The combination of quotation and unquotation is called quasiquotation.
Unquotation provides a way to refer to variables during quotation.
Variables are problematic when quoting because a captured
expression is essentially a constant, just like a string is a
constant. For instance in all the following cases apple
is a
constant: ~apple
, "apple"
and expr(apple)
. Unquoting allows
you to introduce a part of variability within a captured
expression.
In the case of enexpr()
and enquo()
, unquoting provides an
escape hatch to the users of your function that allows them to
manipulate the expression that you capture.
In the case of expr()
and quo()
, quasiquotation lets you
build a complex expressions where some parts are constant (the
parts that are captured) and some parts are variable (the parts
that are unquoted).
See the quasiquotation help topic for more about this as well as the chapter in Advanced R.
All the quotation functions mentioned here are stable.
# expr() and exprs() capture expressions that you supply: expr(symbol) exprs(several, such, symbols) # enexpr() and enexprs() capture expressions that your user supplied: expr_inputs <- function(arg, ...) { user_exprs <- enexprs(arg, ...) user_exprs } expr_inputs(hello) expr_inputs(hello, bonjour, ciao) # ensym() and ensyms() provide additional type checking to ensure # the user calling your function has supplied bare object names: sym_inputs <- function(...) { user_symbols <- ensyms(...) user_symbols } sym_inputs(hello, "bonjour") ## sym_inputs(say(hello)) # Error: Must supply symbols or strings expr_inputs(say(hello)) # All these quoting functions have quasiquotation support. This # means that you can unquote (evaluate and inline) part of the # captured expression: what <- sym("bonjour") expr(say(what)) expr(say(!!what)) # This also applies to the expressions supplied the user. This is # like an escape hatch that allows control over the captured # expression: expr_inputs(say(!!what), !!what) # Finally, you can capture expressions as quosures. A quosure is an # object that contains both the expression and its environment: quo <- quo(letters) quo get_expr(quo) get_env(quo) # Quosures can be evaluated with eval_tidy(): eval_tidy(quo) # They have the nice property that you can pass them around from # context to context (that is, from function to function) and they # still evaluate in their original environment: multiply_expr_by_10 <- function(expr) { # We capture the user expression and its environment: expr <- enquo(expr) # Then create an object that only exists in this function: local_ten <- 10 # Now let's create a multiplication expression that (a) inlines # the user expression as LHS (still wrapped in its quosure) and # (b) refers to the local object in the RHS: quo(!!expr * local_ten) } quo <- multiply_expr_by_10(2 + 3) # The local parts of the quosure are printed in colour if your # terminal is capable of displaying colours: quo # All the quosures in the expression evaluate in their original # context. The local objects are looked up properly and we get the # expected result: eval_tidy(quo)