% Generated by roxygen2: do not edit by hand % Please edit documentation in R/topic-nse.R \name{topic-embrace-constants} \alias{topic-embrace-constants} \title{Why are strings and other constants enquosed in the empty environment?} \description{ Function arguments are \link[=topic-defuse]{defused} into \link[=topic-quosure]{quosures} that keep track of the environment of the defused expression. \if{html}{\out{
}}\preformatted{quo(1 + 1) #> #> expr: ^1 + 1 #> env: global }\if{html}{\out{
}} You might have noticed that when constants are supplied, the quosure tracks the empty environment instead of the current environmnent. \if{html}{\out{
}}\preformatted{quos("foo", 1, NULL) #> > #> #> [[1]] #> #> expr: ^"foo" #> env: empty #> #> [[2]] #> #> expr: ^1 #> env: empty #> #> [[3]] #> #> expr: ^NULL #> env: empty }\if{html}{\out{
}} The reason for this has to do with compilation of R code which makes it impossible to consistently capture environments of constants from function arguments. Argument defusing relies on the \emph{promise} mechanism of R for lazy evaluation of arguments. When functions are compiled and R notices that an argument is constant, it avoids creating a promise since they slow down function evaluation. Instead, the function is directly supplied a naked constant instead of constant wrapped in a promise. } \section{Concrete case of promise unwrapping by compilation}{ We can observe this optimisation by calling into the C-level \code{findVar()} function to capture promises. \if{html}{\out{
}}\preformatted{# Return the object bound to `arg` without triggering evaluation of # promises f <- function(arg) \{ rlang:::find_var(current_env(), sym("arg")) \} # Call `f()` with a symbol or with a constant g <- function(symbolic) \{ if (symbolic) \{ f(letters) \} else \{ f("foo") \} \} # Make sure these small functions are compiled f <- compiler::cmpfun(f) g <- compiler::cmpfun(g) }\if{html}{\out{
}} When \code{f()} is called with a symbolic argument, we get the promise object created by R. \if{html}{\out{
}}\preformatted{g(symbolic = TRUE) #> }\if{html}{\out{
}} However, supplying a constant to \code{"f"} returns the constant directly. \if{html}{\out{
}}\preformatted{g(symbolic = FALSE) #> [1] "foo" }\if{html}{\out{
}} Without a promise, there is no way to figure out the original environment of an argument. } \section{Do we need environments for constants?}{ Data-masking APIs in the tidyverse are intentionally designed so that they don't need an environment for constants. \itemize{ \item Data-masking APIs should be able to interpret constants. These can arise from normal argument passing as we have seen, or by \link[=topic-inject]{injection} with \verb{!!}. There should be no difference between \code{dplyr::mutate(mtcars, var = cyl)} and \code{dplyr::mutate(mtcars, var = !!mtcars$cyl)}. \item Data-masking is an \emph{evaluation} idiom, not an \emph{introspective} one. The behaviour of data-masking function should not depend on the calling environment when a constant (or a symbol evaluating to a given value) is supplied. } } \keyword{internal}