I’m still figuring out the new quosure (quotation + closure) system in rlang. This document has some notes on a problem I had to sort out.

I was working on function that took an unquoted column name as an argument. But I also wanted to set NULL as a default value if the user did not provide a column name.

The general idea and usage would be something like…

my_function <- function(data, column = NULL) {
  var <- enquo(column)
  # ... do things...
}

# We want to support both of these calls
my_function(iris)
my_function(iris, Species)

How do I check whether or not the column is NULL or a column name once it has been enquo’d? rlang provides several functions to check the expression inside of a quosure.

library(rlang)

f <- function(var) {
  v <- enquo(var)
  str(list(
    v = v,
    quo_is_missing = quo_is_missing(v),
    quo_is_null = quo_is_null(v),
    quo_is_symbol = quo_is_symbol(v),
    quo_is_symbolic = quo_is_symbolic(v),
    quo_is_lang = quo_is_lang(v)))
}


f()
#> List of 6
#>  $ v              :<quosure: empty>
#> ~
#>  $ quo_is_missing : logi TRUE
#>  $ quo_is_null    : logi FALSE
#>  $ quo_is_symbol  : logi TRUE
#>  $ quo_is_symbolic: logi TRUE
#>  $ quo_is_lang    : logi FALSE
f(Time)
#> List of 6
#>  $ v              :<quosure: global>
#> ~Time
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi FALSE
#>  $ quo_is_symbol  : logi TRUE
#>  $ quo_is_symbolic: logi TRUE
#>  $ quo_is_lang    : logi FALSE
f(NULL)
#> List of 6
#>  $ v              :<quosure: empty>
#> ~NULL
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi TRUE
#>  $ quo_is_symbol  : logi FALSE
#>  $ quo_is_symbolic: logi FALSE
#>  $ quo_is_lang    : logi FALSE

g <- function(var = NULL) {
  v <- enquo(var)
  str(list(
    v = v,
    quo_is_missing = quo_is_missing(v),
    quo_is_null = quo_is_null(v),
    quo_is_symbol = quo_is_symbol(v),
    quo_is_symbolic = quo_is_symbolic(v),
    quo_is_lang = quo_is_lang(v)))
}

g()
#> List of 6
#>  $ v              :<quosure: empty>
#> ~NULL
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi TRUE
#>  $ quo_is_symbol  : logi FALSE
#>  $ quo_is_symbolic: logi FALSE
#>  $ quo_is_lang    : logi FALSE
g(Time)
#> List of 6
#>  $ v              :<quosure: global>
#> ~Time
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi FALSE
#>  $ quo_is_symbol  : logi TRUE
#>  $ quo_is_symbolic: logi TRUE
#>  $ quo_is_lang    : logi FALSE
g(NULL)
#> List of 6
#>  $ v              :<quosure: empty>
#> ~NULL
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi TRUE
#>  $ quo_is_symbol  : logi FALSE
#>  $ quo_is_symbolic: logi FALSE
#>  $ quo_is_lang    : logi FALSE

What to do when these functions are sending quos to other functions? Unquote them if they are being re-enquo’d.

Without unquoting, the inner function just re-quotes v. Note that the environment <quosure: ...> is neither global nor empty; v’s environment is the outer function.

h_send_quo <- function(var) {
    v <- enquo(var)
    f(v)
}

h_send_quo()
#> List of 6
#>  $ v              :<quosure: frame 0000000008BB8128>
#> ~v
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi FALSE
#>  $ quo_is_symbol  : logi TRUE
#>  $ quo_is_symbolic: logi TRUE
#>  $ quo_is_lang    : logi FALSE
h_send_quo(Time)
#> List of 6
#>  $ v              :<quosure: frame 000000000883F658>
#> ~v
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi FALSE
#>  $ quo_is_symbol  : logi TRUE
#>  $ quo_is_symbolic: logi TRUE
#>  $ quo_is_lang    : logi FALSE
h_send_quo(NULL)
#> List of 6
#>  $ v              :<quosure: frame 000000000AF44378>
#> ~v
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi FALSE
#>  $ quo_is_symbol  : logi TRUE
#>  $ quo_is_symbolic: logi TRUE
#>  $ quo_is_lang    : logi FALSE

With unquoting, the inner function see the expression quoted by v.

h_send_unquoted <- function(var) {
    v <- enquo(var)
    f(!! v)
}
h_send_unquoted()
#> List of 6
#>  $ v              :<quosure: empty>
#> ~
#>  $ quo_is_missing : logi TRUE
#>  $ quo_is_null    : logi FALSE
#>  $ quo_is_symbol  : logi TRUE
#>  $ quo_is_symbolic: logi TRUE
#>  $ quo_is_lang    : logi FALSE
h_send_unquoted(Time)
#> List of 6
#>  $ v              :<quosure: global>
#> ~Time
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi FALSE
#>  $ quo_is_symbol  : logi TRUE
#>  $ quo_is_symbolic: logi TRUE
#>  $ quo_is_lang    : logi FALSE
h_send_unquoted(NULL)
#> List of 6
#>  $ v              :<quosure: empty>
#> ~NULL
#>  $ quo_is_missing : logi FALSE
#>  $ quo_is_null    : logi TRUE
#>  $ quo_is_symbol  : logi FALSE
#>  $ quo_is_symbolic: logi FALSE
#>  $ quo_is_lang    : logi FALSE