Reading — step 1 of 5
Learn
R has non-standard evaluation (NSE) — functions that capture their arguments unevaluated and operate on the expression itself. This is what makes subset(df, age > 30) work — age > 30 is captured as code, not evaluated in the calling scope.
quote() captures an unevaluated expression:
expr <- quote(x + 1)
expr # x + 1
class(expr) # "call"
x <- 10
eval(expr) # 11
eval(expr, list(x = 99)) # 100
substitute() captures an argument unevaluated:
show <- function(x) {
e <- substitute(x)
cat("called with:", deparse(e), "\n")
}
show(1 + 2) # "called with: 1 + 2"
show(mean(c(1,2))) # "called with: mean(c(1, 2))"
The expression isn't evaluated — show sees the parse tree.
deparse() turns an expression back into a string:
deparse(quote(x + y * 2)) # "x + y * 2"
bquote() is quote with substitution:
x <- 5
expr <- bquote(.(x) + y) # 5 + y — x was substituted
Tidy evaluation (rlang, used by tidyverse):
library(rlang)
filter_col <- function(df, col, val) {
col_quo <- enquo(col) # capture the expression
df |> dplyr::filter(!!col_quo == val)
}
filter_col(df, age, 30) # filters where age == 30
enquo captures the unevaluated argument; !! ("bang-bang") splices it back in. This is how dplyr knows what age means without age being defined in the caller's scope.
Why NSE matters:
- Domain-specific languages:
dplyr,ggplot2,lm(formulas) - Better error messages: include the original expression
- Lazy evaluation: only evaluate if needed
Why it's tricky:
- Programmatic use is awkward — can't pass
colas a variable easily - Two solutions: tidy eval (
!!,{{ }}) orsubstitute+eval - Debugging is harder — the expression is delayed
Common patterns:
Capture expression for an error message:
assert_that <- function(cond) {
if (!cond) {
e <- substitute(cond)
stop("failed: ", deparse(e))
}
}
assert_that(1 + 1 == 3) # Error: failed: 1 + 1 == 3
Build expressions dynamically:
cols <- c("x", "y", "z")
formula_str <- paste("y ~", paste(cols[-1], collapse = " + "))
formula <- as.formula(formula_str)
# y ~ x + z
lm(formula, data = df)
NSE is one of R's most powerful (and confusing) features. Tidy evaluation in rlang is the modern recommended approach.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…