Expressions

2026-02-19

Literally everything in R is an object

… even the language itself

x <- 5
e <- expr(2*x)

e
2 * x
eval(e)
[1] 10

e is called a defused or quoted expression (“unevaluated expression”, “call”, “blueprint”, “recipe”).

Foremost e is a language object

is.language(e)
[1] TRUE
is.call(e)
[1] TRUE

Expressions are list-like

x <- 1:10
e <- expr(x[x%%3==0])

Expressions have multiple parts:

length(e)
[1] 3
as.list(e)
[[1]]
`[`

[[2]]
x

[[3]]
x%%3 == 0

Expressions have a tree structure

e is of class call (and has type language)

as.list(e)
[[1]]
`[`

[[2]]
x

[[3]]
x%%3 == 0
as.list(e) |> sapply(FUN = class)
[1] "name" "name" "call"
as.list(e) |> sapply(FUN = typeof)
[1] "symbol"   "symbol"   "language"

Into the rabbit hole or up the tree?

e[[3]] is of class call (and has type language)

as.list(e[[3]])
[[1]]
`==`

[[2]]
x%%3

[[3]]
[1] 0
as.list(e[[3]]) |> sapply(FUN = class)
[1] "name"    "call"    "numeric"
as.list(e[[3]]) |> sapply(FUN = typeof)
[1] "symbol"   "language" "double"  

The Abstract Structure Tree

Language objects form an abstract tree of pre-fix functions:

library(lobstr)
ast(x[x%%3==0])
█─`[` 
├─x 
└─█─`==` 
  ├─█─`%%` 
  │ ├─x 
  │ └─3 
  └─0 

All leaves are either constants (0, 3) or symbols (x)

Nodes are functions (operators are infix functions) ([, ==, %%)

An Expression is …

any element of the following inductively defined set:

  • a constant (like 1, 2, pi)
expr(1) |> str()
 num 1
  • a symbol (like x, mtcars, diamonds)
expr(mtcars) |> str()
 symbol mtcars
  • or a call, mathematical operation or function call
expr(x^2) |> str()
 language x^2

Base R also allows vectors of expressions:

as.expression, expression, and is.expression assume vectors of expressions … and react a bit differently.

is.expression(as.expression(exp(1)))
[1] TRUE
is_expression(as.expression(exp(1)))
[1] FALSE
is.expression(expr(exp(1)))
[1] FALSE
is_expression(expr(exp(1)))
[1] TRUE

We will stick to rlang definitions (functions with _)

Evaluating unevaluated expressions

Unevaluated expressions can be evaluated … result depends on environment in which they are evaluated

eval(e) # default environment is parent.frame() - i.e.
[1] 3 6 9
# where call to eval was made

eval(e, envir=env(x=pi))
numeric(0)

Strings and Symbols

ast(x <- "x")
█─`<-` 
├─x 
└─"x" 

Symbols are (names of) objects - represented without quotes

Strings are shown with quotes

sym takes as input a character and returns a symbol

sym("cyl") 
cyl
eval(sym("cyl"))
Error:
! object 'cyl' not found
eval(sym("cyl"), envir = mtcars)
 [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4

Names of symbols and multiple symbols

We get the name of a symbol back using as_string:

as_string(sym("x"))
[1] "x"

… and we create multiple symbols using syms:

syms creates a list of symbols, i.e. 

syms(paste("means", LETTERS[1:5], sep = "_"))
[[1]]
means_A

[[2]]
means_B

[[3]]
means_C

[[4]]
means_D

[[5]]
means_E

Programmatic use

… in interactive use sym, syms or as_string are not very useful …

making a function call base::call or rlang::call2

call("f", 1, 2, 3)
f(1, 2, 3)
call2("f", 1, 2, call2("+", 1, 2))
f(1, 2, 1 + 2)

Your Turn

Can we write the following expressions programmatically?

means_D = diamonds %>% filter(color == "D")
means_E = diamonds %>% filter(color == "E")
means_F = diamonds %>% filter(color == "F")
means_G = diamonds %>% filter(color == "G")
means_H = diamonds %>% filter(color == "H")

all but the assignment:

library(ggplot2)
diam_color <- function (col) {
  diamonds |> dplyr::filter(color == col)
}

lapply(LETTERS[4:8], diam_color)
[[1]]
# A tibble: 6,775 × 10
   carat cut       color clarity depth table price     x     y     z
   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
 1  0.23 Very Good D     VS2      60.5    61   357  3.96  3.97  2.4 
 2  0.23 Very Good D     VS1      61.9    58   402  3.92  3.96  2.44
 3  0.26 Very Good D     VS2      60.8    59   403  4.13  4.16  2.52
 4  0.26 Good      D     VS2      65.2    56   403  3.99  4.02  2.61
 5  0.26 Good      D     VS1      58.4    63   403  4.19  4.24  2.46
 6  0.22 Premium   D     VS2      59.3    62   404  3.91  3.88  2.31
 7  0.3  Premium   D     SI1      62.6    59   552  4.23  4.27  2.66
 8  0.3  Ideal     D     SI1      62.5    57   552  4.29  4.32  2.69
 9  0.3  Ideal     D     SI1      62.1    56   552  4.3   4.33  2.68
10  0.24 Very Good D     VVS1     61.5    60   553  3.97  4     2.45
# ℹ 6,765 more rows

[[2]]
# A tibble: 9,797 × 10
   carat cut       color clarity depth table price     x     y     z
   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
 1  0.23 Ideal     E     SI2      61.5    55   326  3.95  3.98  2.43
 2  0.21 Premium   E     SI1      59.8    61   326  3.89  3.84  2.31
 3  0.23 Good      E     VS1      56.9    65   327  4.05  4.07  2.31
 4  0.22 Fair      E     VS2      65.1    61   337  3.87  3.78  2.49
 5  0.2  Premium   E     SI2      60.2    62   345  3.79  3.75  2.27
 6  0.32 Premium   E     I1       60.9    58   345  4.38  4.42  2.68
 7  0.23 Very Good E     VS2      63.8    55   352  3.85  3.92  2.48
 8  0.23 Very Good E     VS1      60.7    59   402  3.97  4.01  2.42
 9  0.23 Very Good E     VS1      59.5    58   402  4.01  4.06  2.4 
10  0.23 Good      E     VS1      64.1    59   402  3.83  3.85  2.46
# ℹ 9,787 more rows

[[3]]
# A tibble: 9,542 × 10
   carat cut       color clarity depth table price     x     y     z
   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
 1  0.22 Premium   F     SI1      60.4    61   342  3.88  3.84  2.33
 2  0.23 Very Good F     VS1      60.9    57   357  3.96  3.99  2.42
 3  0.23 Very Good F     VS1      60      57   402  4     4.03  2.41
 4  0.23 Very Good F     VS1      59.8    57   402  4.04  4.06  2.42
 5  0.23 Good      F     VS1      58.2    59   402  4.06  4.08  2.37
 6  0.29 Premium   F     SI1      62.4    58   403  4.24  4.26  2.65
 7  0.24 Very Good F     SI1      60.9    61   404  4.02  4.03  2.45
 8  0.26 Very Good F     VVS2     59.2    60   554  4.19  4.22  2.49
 9  0.7  Good      F     VS1      59.4    62  2759  5.71  5.76  3.4 
10  0.96 Fair      F     SI2      66.3    62  2759  6.27  5.95  4.07
# ℹ 9,532 more rows

[[4]]
# A tibble: 11,292 × 10
   carat cut       color clarity depth table price     x     y     z
   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
 1  0.23 Very Good G     VVS2     60.4    58   354  3.97  4.01  2.41
 2  0.23 Ideal     G     VS1      61.9    54   404  3.93  3.95  2.44
 3  0.28 Ideal     G     VVS2     61.4    56   553  4.19  4.22  2.58
 4  0.31 Very Good G     SI1      63.3    57   553  4.33  4.3   2.73
 5  0.31 Premium   G     SI1      61.8    58   553  4.35  4.32  2.68
 6  0.24 Premium   G     VVS1     62.3    59   554  3.95  3.92  2.45
 7  0.7  Ideal     G     VS2      61.6    56  2757  5.7   5.67  3.5 
 8  0.78 Very Good G     SI2      63.8    56  2759  5.81  5.85  3.72
 9  0.74 Ideal     G     SI1      61.6    55  2760  5.8   5.85  3.59
10  0.75 Premium   G     VS2      61.7    58  2760  5.85  5.79  3.59
# ℹ 11,282 more rows

[[5]]
# A tibble: 8,304 × 10
   carat cut       color clarity depth table price     x     y     z
   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
 1  0.26 Very Good H     SI1      61.9    55   337  4.07  4.11  2.53
 2  0.23 Very Good H     VS1      59.4    61   338  4     4.05  2.39
 3  0.23 Very Good H     VS1      61      57   353  3.94  3.96  2.41
 4  0.31 Good      H     SI1      64      54   402  4.29  4.31  2.75
 5  0.32 Good      H     SI2      63.1    56   403  4.34  4.37  2.75
 6  0.32 Very Good H     SI2      61.8    55   403  4.35  4.42  2.71
 7  0.32 Good      H     SI2      63.8    56   403  4.36  4.38  2.79
 8  0.29 Very Good H     SI2      60.7    60   404  4.33  4.37  2.64
 9  0.3  Very Good H     SI1      63.1    56   554  4.29  4.27  2.7 
10  0.3  Premium   H     SI1      62.9    59   554  4.28  4.24  2.68
# ℹ 8,294 more rows

call2("<-", sym("x"), 1)
x <- 1
long_list <- lapply(LETTERS[4:8], function(x) {
  call2("<<-", sym(paste("diamonds", x, sep="_")), diam_color(x)) |> eval()
})

head(diamonds_D)
# A tibble: 6 × 10
  carat cut       color clarity depth table price     x     y     z
  <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
1  0.23 Very Good D     VS2      60.5    61   357  3.96  3.97  2.4 
2  0.23 Very Good D     VS1      61.9    58   402  3.92  3.96  2.44
3  0.26 Very Good D     VS2      60.8    59   403  4.13  4.16  2.52
4  0.26 Good      D     VS2      65.2    56   403  3.99  4.02  2.61
5  0.26 Good      D     VS1      58.4    63   403  4.19  4.24  2.46
6  0.22 Premium   D     VS2      59.3    62   404  3.91  3.88  2.31