18.1 Introduction

To compute on the language, we first need to understand its structure. That requires some new vocabulary, some new tools, and some new ways of thinking about R code. The first of these is the distinction between an operation and its result. Take the following code, which multiplies a variable x by 10 and saves the result to a new variable called y. It doesn’t work because we haven’t defined a variable called x:

y <- x * 10
#> Error in eval(expr, envir, enclos): object 'x' not found

It would be nice if we could capture the intent of the code without executing it. In other words, how can we separate our description of the action from the action itself?

One way is to use rlang::expr():

z <- rlang::expr(y <- x * 10)
z
#> y <- x * 10

expr() returns an expression, an object that captures the structure of the code without evaluating it (i.e. running it). If you have an expression, you can evaluate it with base::eval():

x <- 4
eval(z)
y
#> [1] 40

The focus of this chapter is the data structures that underlie expressions. Mastering this knowledge will allow you to inspect and modify captured code, and to generate code with code. We’ll come back to expr() in Chapter 19, and to eval() in Chapter 20.

Outline

  • Section 18.2 introduces the idea of the abstract syntax tree (AST), and reveals the tree like structure that underlies all R code.

  • Section 18.3 dives into the details of the data structures that underpin the AST: constants, symbols, and calls, which are collectively known as expressions.

  • Section 18.4 covers parsing, the act of converting the linear sequence of character in code into the AST, and uses that idea to explore some details of R’s grammar.

  • Section 18.5 shows you how you can use recursive functions to compute on the language, writing functions that compute with expressions.

  • Section 18.6 circles back to three more specialised data structures: pairlists, missing arguments, and expression vectors.

Prerequisites

Make sure you’ve read the metaprogramming overview in Chapter 17 to get a broad overview of the motivation and the basic vocabulary. You’ll also need the rlang package to capture and compute on expressions, and the lobstr package to visualise them.

library(rlang)
library(lobstr)