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