```{r, child = "setup.Rmd", include = FALSE} ``` The injection operators are extensions of R implemented by rlang to modify a piece of code before R processes it. There are two main families: - The [dynamic dots][dyn-dots] operators, [`!!!`] and `r link("'{'")`. - The [metaprogramming operators][topic-metaprogramming] [`!!`], `r link("{{")`, and `r link("'{{'")`. Splicing with [`!!!`] can also be done in metaprogramming context. # Dots injection Unlike regular `...`, [dynamic dots][dyn-dots] are programmable with injection operators. ## Splicing with `!!!` For instance, take a function like `rbind()` which takes data in `...`. To bind rows, you supply them as separate arguments: ```{r, comment = "#>", collapse = TRUE} rbind(a = 1:2, b = 3:4) ``` But how do you bind a variable number of rows stored in a list? The base R solution is to invoke `rbind()` with `do.call()`: ```{r, comment = "#>", collapse = TRUE} rows <- list(a = 1:2, b = 3:4) do.call("rbind", rows) ``` Functions that implement dynamic dots include a built-in way of folding a list of arguments in `...`. To illustrate this, we'll create a variant of `rbind()` that takes dynamic dots by collecting `...` with [list2()]: ```{r, comment = "#>", collapse = TRUE} rbind2 <- function(...) { do.call("rbind", list2(...)) } ``` It can be used just like `rbind()`: ```{r, comment = "#>", collapse = TRUE} rbind2(a = 1:2, b = 3:4) ``` And a list of arguments can be supplied by _splicing_ the list with [`!!!`]: ```{r, comment = "#>", collapse = TRUE} rbind2(!!!rows, c = 5:6) ``` ## Injecting names with \verb{"\{"} A related problem comes up when an argument name is stored in a variable. With dynamic dots, you can inject the name using [glue syntax](https://glue.tidyverse.org/) with `r link("'{'")`: ```{r, comment = "#>", collapse = TRUE} name <- "foo" rbind2("{name}" := 1:2, bar = 3:4) rbind2("prefix_{name}" := 1:2, bar = 3:4) ``` # Metaprogramming injection [Data-masked][topic-data-mask] arguments support the following injection operators. They can also be explicitly enabled with [inject()]. ## Embracing with `{{` The embracing operator `r link("{{")` is made specially for function arguments. It [defuses][topic-defuse] the expression supplied as argument and immediately injects it in place. The injected argument is then evaluated in another context such as a [data mask][topic-data-mask]. ```{r, comment = "#>", collapse = TRUE} # Inject function arguments that might contain # data-variables by embracing them with {{ }} mean_by <- function(data, by, var) { data %>% dplyr::group_by({{ by }}) %>% dplyr::summarise(avg = mean({{ var }}, na.rm = TRUE)) } # The data-variables `cyl` and `disp` inside the # env-variables `by` and `var` are injected inside `group_by()` # and `summarise()` mtcars %>% mean_by(by = cyl, var = disp) ``` Learn more about this pattern in `r link("topic_data_mask_programming")`. ## Injecting with `!!` Unlike [`!!!`] which injects a list of arguments, the injection operator [`!!`] (pronounced "bang-bang") injects a _single_ object. One use case for `!!` is to substitute an environment-variable (created with `<-`) with a data-variable (inside a data frame). ```{r, comment = "#>", collapse = TRUE} # The env-variable `var` contains a data-symbol object, in this # case a reference to the data-variable `height` var <- data_sym("disp") # We inject the data-variable contained in `var` inside `summarise()` mtcars %>% dplyr::summarise(avg = mean(!!var, na.rm = TRUE)) ``` Another use case is to inject a variable by value to avoid [name collisions][topic-data-mask-ambiguity]. ```{r, comment = "#>", collapse = TRUE} df <- data.frame(x = 1) # This name conflicts with a column in `df` x <- 100 # Inject the env-variable df %>% dplyr::mutate(x = x / !!x) ``` Note that in most cases you don't need injection with `!!`. For instance, the [`.data`] and [`.env`] pronouns provide more intuitive alternatives to injecting a column name and injecting a value. ## Splicing with `!!!` The splice operator [`!!!`] of dynamic dots can also be used in metaprogramming context (inside [data-masked][topic-data-mask] arguments and inside [inject()]). For instance, we could reimplement the `rbind2()` function presented above using `inject()` instead of `do.call()`: ```r rbind2 <- function(...) { inject(rbind(!!!list2(...))) } ``` There are two things going on here. We collect `...` with [list2()] so that the callers of `rbind2()` may use `!!!`. And we use `inject()` so that `rbind2()` itself may use `!!!` to splice the list of arguments passed to `rbind2()`. # Injection in other languages Injection is known as __quasiquotation__ in other programming languages and in computer science. `expr()` is similar to a quasiquotation operator and `!!` is the unquote operator. These terms have a rich history in Lisp languages, and live on in modern languages like [Julia](https://docs.julialang.org/en/v1/manual/metaprogramming/) and [Racket](https://docs.racket-lang.org/reference/quasiquote.html). In base R, quasiquotation is performed with [bquote()]. The main difference between rlang and other languages is that quasiquotation is often implicit instead of explicit. You can use injection operators in any defusing / quoting function (unless that function defuses its argument with a special operator like [enquo0()]). This is not the case in lisp languages for example where injection / unquoting is explicit and only enabled within a backquote. # See also - `r link("topic_inject_out_of_context")`