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.

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

# quo() is a quotation function just like expr() and quote(): expr(mean(1:10 * 2))
#> mean(1:10 * 2)
quo(mean(1:10 * 2))
#> <quosure: local> #> ~mean(1:10 * 2)
# It supports quasiquotation and allows unquoting (evaluating # immediately) part of the quoted expression: quo(mean(!! 1:10 * 2))
#> <quosure: local> #> ~mean(c(2, 4, 6, 8, 10, 12, 14, 16, 18, 20))
# 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
#> <quosure: local> #> ~var
# Here `quo` quotes `var`. Let's check that it also captures the # environment where that symbol is defined: identical(get_env(quo), get_env())
#> [1] TRUE
env_has(quo, "var")
#> [1] TRUE
# 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
#> <quosure: local> #> ~foobar * 2
# `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)
#> [1] 20
# Like other formulas, quosures are normally self-quoting under # evaluation: eval(~var)
#> ~var #> <environment: 0x7f979ac90608>
eval(quo(var))
#> <quosure: local> #> ~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)
#> ~var #> <environment: 0x7f979ac90608>
eval_tidy(quo(var))
#> [1] "foo"
# 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
#> <quosure: local> #> ~toupper(~letters)
#> [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"
# 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)))
#> <quosure: local> #> ~nse_function(~foo(bar))
quo(nse_function(UQE(var)))
#> <quosure: local> #> ~nse_function(foo(bar))
# This is equivalent to unquoting and taking the RHS: quo(nse_function(!! get_expr(var)))
#> <quosure: local> #> ~nse_function(foo(bar))
# 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))
#> <quosure: local> #> ~mtcars$cyl
# `!!`() 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))
#> <quosure: local> #> ~mtcars$cyl
# 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))
#> <quosure: local> #> ~foo
# Literals are enquosed with the empty environment because they can # be evaluated anywhere. The brackets indicate "empty": quo(10L)
#> <quosure: empty> #> ~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
#> <quosure: local> #> ~foo
#> <quosure: local> #> ~foo
str(quo1); str(quo2)
#> <quosure: local 0x7f979e79f2e0> #> ~foo
#> <quosure: local 0x7f97a196ca78> #> ~foo
# You can also see this address by printing the environment at the # console: get_env(quo1)
#> <environment: 0x7f979e79f2e0>
get_env(quo2)
#> <environment: 0x7f97a196ca78>
# new_quosure() takes by value an expression that is already quoted: expr <- quote(mtcars) env <- as_env("datasets") quo <- new_quosure(expr, env) quo
#> <quosure: local> #> ~mtcars
#> mpg cyl disp hp drat wt qsec vs am gear carb #> Mazda RX4 21.0 6 160.0 110 3.90 2.62 16.5 0 1 4 4 #> Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.88 17.0 0 1 4 4 #> Datsun 710 22.8 4 108.0 93 3.85 2.32 18.6 1 1 4 1 #> Hornet 4 Drive 21.4 6 258.0 110 3.08 3.21 19.4 1 0 3 1 #> Hornet Sportabout 18.7 8 360.0 175 3.15 3.44 17.0 0 0 3 2 #> Valiant 18.1 6 225.0 105 2.76 3.46 20.2 1 0 3 1 #> Duster 360 14.3 8 360.0 245 3.21 3.57 15.8 0 0 3 4 #> Merc 240D 24.4 4 146.7 62 3.69 3.19 20.0 1 0 4 2 #> Merc 230 22.8 4 140.8 95 3.92 3.15 22.9 1 0 4 2 #> Merc 280 19.2 6 167.6 123 3.92 3.44 18.3 1 0 4 4 #> Merc 280C 17.8 6 167.6 123 3.92 3.44 18.9 1 0 4 4 #> Merc 450SE 16.4 8 275.8 180 3.07 4.07 17.4 0 0 3 3 #> Merc 450SL 17.3 8 275.8 180 3.07 3.73 17.6 0 0 3 3 #> Merc 450SLC 15.2 8 275.8 180 3.07 3.78 18.0 0 0 3 3 #> Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.25 18.0 0 0 3 4 #> Lincoln Continental 10.4 8 460.0 215 3.00 5.42 17.8 0 0 3 4 #> Chrysler Imperial 14.7 8 440.0 230 3.23 5.34 17.4 0 0 3 4 #> Fiat 128 32.4 4 78.7 66 4.08 2.20 19.5 1 1 4 1 #> Honda Civic 30.4 4 75.7 52 4.93 1.61 18.5 1 1 4 2 #> Toyota Corolla 33.9 4 71.1 65 4.22 1.83 19.9 1 1 4 1 #> Toyota Corona 21.5 4 120.1 97 3.70 2.46 20.0 1 0 3 1 #> Dodge Challenger 15.5 8 318.0 150 2.76 3.52 16.9 0 0 3 2 #> AMC Javelin 15.2 8 304.0 150 3.15 3.44 17.3 0 0 3 2 #> Camaro Z28 13.3 8 350.0 245 3.73 3.84 15.4 0 0 3 4 #> Pontiac Firebird 19.2 8 400.0 175 3.08 3.85 17.1 0 0 3 2 #> Fiat X1-9 27.3 4 79.0 66 4.08 1.94 18.9 1 1 4 1 #> Porsche 914-2 26.0 4 120.3 91 4.43 2.14 16.7 0 1 5 2 #> Lotus Europa 30.4 4 95.1 113 3.77 1.51 16.9 1 1 5 2 #> Ford Pantera L 15.8 8 351.0 264 4.22 3.17 14.5 0 1 5 4 #> Ferrari Dino 19.7 6 145.0 175 3.62 2.77 15.5 0 1 5 6 #> Maserati Bora 15.0 8 301.0 335 3.54 3.57 14.6 0 1 5 8 #> Volvo 142E 21.4 4 121.0 109 4.11 2.78 18.6 1 1 4 2