Defusing a function argument prevents its evaluation. When a
function argument is defused, R doesn't return its value like it
normally would but it returns the R expression describing how to
make the value. These defused expressions are like blueprints for
computing values. (Defusing is also known as quoting, and is done
in base R by quote() and substitute().)
There are two main ways to defuse expressions, to which correspond
the two functions expr() and enquo(). Whereas expr() defuses
your own expression, enquo() defuses expressions supplied as
argument by the user of a function. See section on function
arguments for more on this distinction.
The main purpose of preventing evaluation of an expression is to avoid "object not found" errors when the expression involves data-variables that only exist in a data frame. The expression is defused so it can be resumed later on, in a context where the data-variables have been defined.
Defusing prevents the evaluation of R code, but you can still force
evaluation inside a defused expression with the forcing operators !! and !!!.
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, .homonyms = c("keep", "first", "last", "error"), .check_assign = FALSE) ensym(arg) ensyms(..., .named = FALSE, .ignore_empty = c("trailing", "none", "all"), .unquote_names = TRUE, .homonyms = c("keep", "first", "last", "error"), .check_assign = FALSE) 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, .homonyms = c("keep", "first", "last", "error"), .check_assign = FALSE)
| 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 |
| .homonyms | How to treat arguments with the same name. The
default, |
| .check_assign | Whether to check for |
Calls, like f(1, 2, 3), 1 + 1, or ..., represent the action of calling a function to compute a
new value, such as a vector.
Symbols, like x or df, represent named objects. When the object pointed to
by the symbol was defined in a function or in the global
environment, we call it an environment-variable. When the object
is a column in a data frame, we call it a data-variable.
You can create new call or symbol objects by using the defusing
function expr():
# Create a symbol representing objects called `foo` expr(foo) # Create a call representing the computation of the mean of `foo` expr(mean(foo, na.rm = TRUE))
Defusing is not the only way to create defused expressions. You can also assemble them from data:
# Assemble a symbol from a string
var <- "foo"
sym(var)
# Assemble a call from strings, symbols, and other objects
call("mean", sym(var), na.rm = TRUE)
There are two points of view when it comes to defusing an expression:
You can defuse expressions that you supply with expr(). This
is one way of creating symbols and calls (see previous section).
You can defuse the expressions supplied by the user of your
function with the operators starting with en like ensym(),
enquo() and their plural variants. They defuse function
arguments .
If you inspect the return values of expr() and enquo(), you'll
notice that the latter doesn't return a raw expression like the
former. Instead it returns a quosure, a wrapper containing an
expression and an environment. R needs information about the
environment to properly evaluate the argument expression because it
comes from a different context than the current function.
See the quosure help topic about tools to work with quosures.
The defusing operator expr() is similar to quote(). Like
bquote(), it allows forcing evaluation of parts
of an expression.
The plural variant exprs() is similar to alist().
The argument-defusing operator enquo() is similar to
substitute().
# expr() and exprs() capture expressions that you supply: expr(symbol)#> symbolexprs(several, such, symbols)#> [[1]] #> several #> #> [[2]] #> such #> #> [[3]] #> symbols #># enexpr() and enexprs() capture expressions that your user supplied: expr_inputs <- function(arg, ...) { user_exprs <- enexprs(arg, ...) user_exprs } expr_inputs(hello)#> [[1]] #> hello #>expr_inputs(hello, bonjour, ciao)#> [[1]] #> hello #> #> [[2]] #> bonjour #> #> [[3]] #> 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")#> [[1]] #> hello #> #> [[2]] #> bonjour #>## sym_inputs(say(hello)) # Error: Must supply symbols or strings expr_inputs(say(hello))#> [[1]] #> 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))#> say(what)expr(say(!!what))#> say(bonjour)# This also applies to expressions supplied by the user. This is # like an escape hatch that allows control over the captured # expression: expr_inputs(say(!!what), !!what)#> [[1]] #> say(bonjour) #> #> [[2]] #> bonjour #># Finally, you can capture expressions as quosures. A quosure is an # object that contains both the expression and its environment: quo <- quo(letters) quo#> <quosure> #> expr: ^letters #> env: 0x5a71908get_expr(quo)#> lettersget_env(quo)#> <environment: 0x5a71908>#> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" #> [20] "t" "u" "v" "w" "x" "y" "z"# 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#> <quosure> #> expr: ^(^2 + 3) * local_ten #> env: 0x67ebc00# 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)#> [1] 50