What is the difference between ensym and enquo when programming with dplyr?

Here is one example illustrating one difference (namely that enquo captures the calling environment and ensym doesn't). Hopefully it speaks for itself:

library(rlang)

f <- function(x) {
  foo <- 42
  print(eval_tidy(quo(!! ensym(x))))
  print(eval_tidy(quo(!! enquo(x))))
}
foo <- 3
f(foo)
# [1] 42
# [1] 3

Or the slightly more convoluted:

library(rlang)

myenv <- new.env()

local(envir = myenv, {
  foo <- 17
  g <- function(x) {
    print(eval_tidy(quo(!! ensym(x))))
    print(eval_tidy(quo(!! enquo(x))))
  }
})

foo <- 123
myenv$g(foo)
#> [1] 17
#> [1] 123

Created on 2019-09-18 by the reprex package (v0.3.0)

The difference is often not noticeable when using dplyr since it is foolproof enough to always look up names in the context of the .data argument first.


Another take :

library(rlang)
library(dplyr, warn.conflicts = FALSE)

test <- function(x){
  Species <- "bar"
  cat("--- enquo builds a quosure from any expression\n")
  print(enquo(x))
  cat("--- ensym captures a symbol or a literal string as a symbol\n")
  print(ensym(x))
  cat("--- evaltidy will evaluate the quosure in its environment\n")
  print(eval_tidy(enquo(x)))
  cat("--- evaltidy will evaluate a symbol locally\n")
  print(eval_tidy(ensym(x)))
  cat("--- but both work fine where the environment doesn't matter\n")
  identical(select(iris,!!ensym(x)), select(iris,!!enquo(x)))
}

Species = "foo"
test(Species)
#> --- enquo builds a quosure from any expression
#> <quosure>
#> expr: ^Species
#> env:  global
#> --- ensym captures a symbol or a literal string as a symbol
#> Species
#> --- evaltidy will evaluate the quosure in its environment
#> [1] "foo"
#> --- evaltidy will evaluate a symbol locally
#> [1] "bar"
#> --- but both work fine where the environment doesn't matter
#> [1] TRUE

test("Species")
#> --- enquo builds a quosure from any expression
#> <quosure>
#> expr: ^"Species"
#> env:  empty
#> --- ensym captures a symbol or a literal string as a symbol
#> Species
#> --- evaltidy will evaluate the quosure in its environment
#> [1] "Species"
#> --- evaltidy will evaluate a symbol locally
#> [1] "bar"
#> --- but both work fine where the environment doesn't matter
#> [1] TRUE
test(paste0("Spec","ies"))
#> --- enquo builds a quosure from any expression
#> <quosure>
#> expr: ^paste0("Spec", "ies")
#> env:  global
#> --- ensym captures a symbol or a literal string as a symbol
#> Only strings can be converted to symbols

Created on 2019-09-23 by the reprex package (v0.3.0)