2026-02-19
a call to library doesn’t need quotes, but would work just fine with them:
a call to install.packages needs a quoted argument or it fails:
Idea of quasiquotation
the combination of
quotation: capturing an expression before evaluation
unquotation: selectively evaluate parts of a (quoted/unevaluated) expression.
… useful for programming (really, meta-programming)
behind a call to library without quotes
takes an argument and puts quotes around it
When we program, we sometimes don’t want the automatic quotes
[1] "Good morning Heike"
[1] "Good time person"
Bang-bang operator !! prevents the automatic quoting:
In the example we call the arguments to paste and like_paste in exactly the opposite way:
Four main functions for quoting (i.e. functions that capture an expression/symbol before it’s evaluated)
Capturing Expressions:
expr and enexprexprs and enexprsCapturing Symbols: sym, ensym, and ensyms (multiple)
expr and enexprexpr is for interactive use:
In a function we are usually more interested in how the function was called, need enriched version of expr:
ensym and ensyms are special cases of enexpr and enexprs that capture the expression(s) and also check that each expression is a symbol:
Can we implement a function my_select now that works the same way as dplyr::select?
my_select <- function (.data, ...) {
vars <- ensyms(...)
# browser()
varnames <- sapply(vars, as_string)
.data[,varnames]
}
mtcars |> my_select("mpg", cyl, disp) |> head() mpg cyl disp
Mazda RX4 21.0 6 160
Mazda RX4 Wag 21.0 6 160
Datsun 710 22.8 4 108
Hornet 4 Drive 21.4 6 258
Hornet Sportabout 18.7 8 360
Valiant 18.1 6 225
my_select also work with indices?Error in `sym()`:
! Can't convert a double vector to a symbol.
ensyms allows only symbols
my_select <- function (.data, ...) {
vars <- enexprs(...)
select_expr <- function(e) {
if (is_symbol(e) | is.numeric(e)) return(.data[,eval(e)])
# more complicated case: we have an expression
browser()
}
lapply(vars, FUN = select_expr) |> data.frame()
}
mtcars |> dplyr::select(1, 2, 3) |> dim()[1] 32 3
[1] 32 3
c.21..21..22.8..21.4..18.7..18.1..14.3..24.4..22.8..19.2..17.8..
1 21.0
2 21.0
3 22.8
4 21.4
5 18.7
6 18.1
c.6..6..4..6..8..6..8..4..4..6..6..8..8..8..8..8..8..4..4..4..
1 6
2 6
3 4
4 6
5 8
6 6
c.160..160..108..258..360..225..360..146.7..140.8..167.6..167.6..
1 160
2 160
3 108
4 258
5 360
6 225
subset …?my_select <- function (.data, ...) {
vars <- enexprs(...)
select_expr <- function(e) {
subset(.data, select = eval(e), subset=T, drop=FALSE)
}
lapply(vars, FUN = select_expr) |> data.frame()
}
mtcars |> my_select(1, 2, 3) |> head() mpg cyl disp
Mazda RX4 21.0 6 160
Mazda RX4 Wag 21.0 6 160
Datsun 710 22.8 4 108
Hornet 4 Drive 21.4 6 258
Hornet Sportabout 18.7 8 360
Valiant 18.1 6 225
Error in `everything()`:
! could not find function "everything"
tidyselect (re-)implements a partial indexing system with :, !, &, | and c()
… we would have the tools though :)
rlangquote acts like expr
alist acts like exprs
substitute is most like enexpr
We will stick with rlang for now.
we have used two functions so far: eval and !!
generally, we use eval to evaluate a whole statement
!! is evaluating individual arguments in functionsUse quasiquotation to construct the following calls:
Note: !!! evaluates all expressions in a list
(x + y)/(y + z)
-(x + z)^(y + z)
x + y + (y + z) - (x + y)
atan(x + y, y + z)
sum(a, b, c)
mean(c(a, b, c), na.rm = TRUE)
foo(a = x + y, b = y + z)