Releases: r-lib/rlang
rlang 0.3.0
Breaking changes
The rlang API is still maturing. In this section, you'll find hard
breaking changes. See the life cycle section below for an exhaustive
list of API changes.
-
quo_text()now deparses non-syntactic symbols with backticks:quo_text(sym("foo+")) #> [1] "`foo+`"This caused a number of issues in reverse dependencies as
quo_text()tends to be used for converting symbols to strings.
quo_text()andquo_name()should not be used for this purpose
because they are general purpose deparsers. These functions should
generally only be used for printing outputs or creating default
labels. If you need to convert symbols to strings, please use
as_string()rather thanquo_text().We have extended the documentation of
?quo_textand?quo_nameto
make these points clearer. -
exprs()no longer flattens quosures.exprs(!!!quos(x, y))is now
equivalent toquos(x, y). -
The sentinel for removing arguments in
call_modify()has been
changed fromNULLtozap(). This breaking change is motivated
by the ambiguity ofNULLwith valid argument values.call_modify(call, arg = NULL) # Add `arg = NULL` to the call call_modify(call, arg = zap()) # Remove the `arg` argument from the call
-
The
%@%operator now quotes its input and supports S4 objects.
This makes it directly equivalent to@except that it extracts
attributes for non-S4 objects (#207). -
Taking the
env_parent()of the empty environment is now an error.
Summary
The changes for this version are organised around three main themes:
error reporting, tidy eval, and tidy dots.
-
abort()now records backtraces automatically in the error object.
Errors thrown withabort()invite users to call
rlang::last_error()to see a backtrace and help identifying where
and why the error occurred. The backtraces created by rlang (you can
create one manually withtrace_back()) are printed in a simplified
form by default that removes implementation details from the
backtrace. To see the full backtrace, call
summary(rlang::last_error()).abort()also gains aparentargument. This is meant for
situations where you're calling a low level API (to download a file,
parse a JSON file, etc) and would like to intercept errors with
base::tryCatch()orrlang::with_handlers()and rethrow them with
a high-level message. Callabort()with the intercepted error as
theparentargument. When the user printsrlang::last_error(),
the backtrace will be shown in two sections corresponding to the
high-level and low-level contexts.In order to get segmented backtraces, the low-level error has to be
thrown withabort(). When that's not the case, you can call the
low-level function withinwith_abort()to automatically promote
all errors to rlang errors. -
The tidy eval changes are mostly for developers of data masking
APIs. The main user-facing change is that.data[[is now an
unquote operator so thatvarin.data[[var]]is never masked by
data frame columns and always picked from the environment. This
makes the pronoun safe for programming in functions. -
The
!!!operator now supports all classed objects like factors. It
callsas.list()on S3 objects andas(x, "list")on S4 objects. -
dots_list()gains several arguments to control how dots are
collected. You can control the selection of arguments with the same
name with.homonyms(keep first, last, all, or abort). You can
also elect to preserve empty arguments with.preserve_empty.
Conditions and errors
-
New
trace_back()captures a backtrace. Compared to the base R
traceback, it contains additional structure about the relationship
between frames. It comes with tools for automatically restricting to
frames after a certain environment on the stack, and to simplify
when printing. These backtraces are now recorded in errors thrown by
abort()(see below). -
abort()gains aparentargument to specify a parent error. This
is meant for situations where a low-level error is expected
(e.g. download or parsing failed) and you'd like to throw an error
with higher level information. Specifying the low-level error as
parent makes it possible to partition the backtraces based on
ancestry. -
Errors thrown with
abort()now embed a backtrace in the condition
object. It is no longer necessary to record a trace with a calling
handler for such errors. -
with_abort()runs expressions in a context where all errors are
promoted to rlang errors and gain a backtrace. -
Unhandled errors thrown by
abort()are now automatically saved and
can be retrieved withrlang::last_error(). The error prints with a
simplified backtrace. Callsummary(last_error())to see the full
backtrace. -
New experimental option
rlang__backtrace_on_errorto display
backtraces alongside error messages. See?rlang::abortfor
supported options. -
The new
signal()function completes theabort(),warn()and
inform()family. It creates and signals a bare condition. -
New
interrupt()function to simulate an user interrupt from R
code. -
cnd_signal()now dispatches messages, warnings, errors and
interrupts to the relevant signalling functions (message(),
warning(),stop()and the C functionRf_onintr()). This makes
it a good choice to resignal a captured condition. -
New
cnd_type()helper to determine the type of a condition
("condition","message","warning","error"or"interrupt"). -
abort(),warn()andinform()now accepts metadata with....
The data are stored in the condition and can be examined by user
handlers.Consequently all arguments have been renamed and prefixed with a dot
(to limit naming conflicts between arguments and metadata names). -
with_handlers()treats bare functions as exiting handlers
(equivalent to handlers supplied totryCatch()). It also supports
the formula shortcut for lambda functions (as in purrr). -
with_handlers()now produces a cleaner stack trace.
Tidy dots
-
The input types of
!!!have been standardised.!!!is generally
defined on vectors: it takes a vector (typically, a list) and
unquotes each element as a separate argument. The standardisation
makes!!!behave the same in functions taking dots withlist2()
and in quoting functions.!!!accepts these types:-
Lists, pairlists, and atomic vectors. If they have a class, they
are converted withbase::as.list()to allow S3 dispatch.
Following this change, objects like factors can now be spliced
without data loss. -
S4 objects. These are converted with
as(obj, "list")before
splicing. -
Quoted blocks of expressions, i.e.
{ }calls
!!!disallows:- Any other objects like functions or environments, but also
language objects like formula, symbols, or quosures.
Quoting functions used to automatically wrap language objects in
lists to make them spliceable. This behaviour is now soft-deprecated
and it is no longer valid to write!!!enquo(x). Please unquote
scalar objects with!!instead. -
-
dots_list(),enexprs()andenquos()gain a.homonyms
argument to control how to treat arguments with the same name.
The default is to keep them. Set it to"first"or"last"to keep
only the first or last occurrences. Set it to"error"to raise an
informative error about the arguments with duplicated names. -
enexprs()andenquos()now support.ignore_empty = "all"
with named arguments as well (#414). -
dots_list()gains a.preserve_emptyargument. WhenTRUE, empty
arguments are stored as missing arguments (see?missing_arg). -
dots_list(),enexprs()andenquos()gain a.check_assign
argument. WhenTRUE, a warning is issued when a<-call is
detected in.... No warning is issued if the assignment is wrapped
in brackets like{ a <- 1 }. The warning lets users know about a
possible typo in their code (assigning instead of matching a
function parameter) and requires them to be explicit that they
really want to assign to a variable by wrapping in parentheses. -
lapply(list(quote(foo)), list2)no longer evaluatesfoo(#580).
Tidy eval
-
You can now unquote quosured symbols as LHS of
:=. The symbol is
automatically unwrapped from the quosure. -
Quosure methods have been defined for common operations like
==. These methods fail with an informative error message
suggesting to unquote the quosure (#478, #tidyverse/dplyr#3476). -
as_data_pronoun()now accepts data masks. If the mask has multiple
environments, all of these are looked up when subsetting the pronoun.
Function objects stored in the mask are bypassed. -
It is now possible to unquote strings in function position. This is
consistent with how the R parser coerces strings to symbols. These
two expressions are now equivalent:expr("foo"())and
expr((!!"foo")()). -
Quosures converted to functions with
as_function()now support
nested quosures. -
expr_deparse()(used to print quosures at the console) now escapes
special characters. For instance, newlines now print as"\n"(#484).
This ensures that the roundtripparse_expr(expr_deparse(x))is not
lossy. -
new_data_mask()now throws an error whenbottomis not a child
oftop(#551). -
Formulas are now evaluated in the correct environment within
eval_tidy(). This fixes issues in dplyr and other tidy-evaluation
interfaces. -
New functions
new_quosures()andas_quosures()to create or
coerce to a list of quosures. This is a small S3 class that ens...
rlang 0.2.2
This is a maintenance release that fixes several garbage collection
protection issues.
rlang 0.2.1
This is a maintenance release that fixes several tidy evaluation
issues.
-
Functions with tidy dots support now allow splicing atomic vectors.
-
Quosures no longer capture the current
srcref. -
Formulas are now evaluated in the correct environment by
eval_tidy(). This fixes issues in dplyr and other tidy-evaluation
interfaces.
rlang 0.2.0
This release of rlang is mostly an effort at polishing the tidy
evaluation framework. All tidy eval functions and operators have been
rewritten in C in order to improve performance. Capture of expression,
quasiquotation, and evaluation of quosures are now vastly faster. On
the UI side, many of the inconveniences that affected the first
release of rlang have been solved:
-
The
!!operator now has the precedence of unary+and-which
allows a much more natural syntax:!!a > bonly unquotesa
rather than the wholea > bexpression. -
enquo()works in magrittr pipes:mtcars %>% select(!!enquo(var)). -
enquos()is a variant ofquos()that has a more natural
interface for capturing multiple arguments and....
See the first section below for a complete list of changes to the tidy
evaluation framework.
This release also polishes the rlang API. Many functions have been
renamed as we get a better feel for the consistency and clarity of the
API. Note that rlang as a whole is still maturing and some functions
are even experimental. In order to make things clearer for users of
rlang, we have started to develop a set of conventions to document the
current stability of each function. You will now find "lifecycle"
sections in documentation topics. In addition we have gathered all
lifecycle information in the ?rlang::lifecycle help page. Please
only use functions marked as stable in your projects unless you are
prepared to deal with occasional backward incompatible updates.
Tidy evaluation
-
The backend for
quos(),exprs(),list2(),dots_list(), etc
is now written in C. This greatly improve the performance of dots
capture, especially with the splicing operator!!!which now
scales much better (you'll see a 1000x performance gain in some
cases). The unquoting algorithm has also been improved which makes
enexpr()andenquo()more efficient as well. -
The tidy eval
!!operator now binds tightly. You no longer have to
wrap it in parentheses, i.e.!!x > ywill only unquotex.Technically the
!!operator has the same precedence as unary-
and+. This means that!!a:band!!a + bare equivalent to
(!!a):band(!!a) + b. On the other hand!!a^band!!a$bare
equivalent to!!(a^b)and!!(a$b). -
The print method for quosures has been greatly improved. Quosures no
longer appear as formulas but as expressions prefixed with^;
quosures are colourised according to their environment; unquoted
objects are displayed between angular brackets instead of code
(i.e. an unquoted integer vector is shown as<int: 1, 2>rather
than1:2); unquoted S3 objects are displayed using
pillar::type_sum()if available. -
New
enquos()function to capture arguments. It treats...the
same way asquos()but can also capture named arguments just like
enquo(), i.e. one level up. By comparisonquos(arg)only
captures the nameargrather than the expression supplied to the
argargument.In addition,
enexprs()is likeenquos()but likeexprs()it
returns bare expressions. Andensyms()expects strings or symbols. -
It is now possible to use
enquo()within a magrittr pipe:select_one <- function(df, var) { df %>% dplyr::select(!!enquo(var)) }Technically, this is because
enquo()now also captures arguments
in parents of the current environment rather than just in the
current environment. The flip side of this increased flexibility is
that if you made a typo in the name of the variable you want to
capture, and if an object of that name exists anywhere in the parent
contexts, you will capture that object rather than getting an error. -
quo_expr()has been renamed toquo_squash()in order to better
reflect that it is a lossy operation that flattens all nested
quosures. -
!!!now accepts any kind of objects for consistency. Scalar types
are treated as vectors of length 1. Previously only symbolic objects
like symbols and calls were treated as such. -
ensym()is a new variant ofenexpr()that expects a symbol or a
string and always returns a symbol. If a complex expression is
supplied it fails with an error. -
exprs()andquos()gain a.unquote_namesarguments to switch
off interpretation of:=as a name operator. This should be useful
for programming on the language targetting APIs such as
data.table. -
exprs()gains a.namedoption to auto-label its arguments (#267). -
Functions taking dots by value rather than by expression
(e.g. regular functions, not quoting functions) have a more
restricted set of unquoting operations. They only support:=and
!!!, and only at top-level. I.e.dots_list(!!! x)is valid but
notdots_list(nested_call(!!! x))(#217). -
Functions taking dots with
list2()ordots_list()now support
splicing ofNULLvalues.!!! NULLis equivalent to!!! list()
(#242). -
Capture operators now support evaluated arguments. Capturing a
forced or evaluated argument is exactly the same as unquoting that
argument: the actual object (even if a vector) is inlined in the
expression. Capturing a forced argument occurs when you use
enquo(),enexpr(), etc too late. It also happens when your
quoting function is supplied tolapply()or when you try to quote
the first argument of an S3 method (which is necessarily evaluated
in order to detect which class to dispatch to). (#295, #300). -
Parentheses around
!!are automatically removed. This makes the
generated expression call cleaner:(!! sym("name"))(arg). Note
that removing the parentheses will never affect the actual
precedence within the expression as the parentheses are only useful
when parsing code as text. The parentheses will also be added by R
when printing code if needed (#296). -
Quasiquotation now supports
!!and!!!as functional forms:expr(`!!`(var)) quo(call(`!!!`(var)))This is consistent with the way native R operators parses to
function calls. These new functional forms are to be preferred to
UQ()andUQS(). We are now questioning the latter and might
deprecate them in a future release. -
The quasiquotation parser now gives meaningful errors in corner
cases to help you figure out what is wrong. -
New getters and setters for quosures:
quo_get_expr(),
quo_get_env(),quo_set_expr(), andquo_set_env(). Compared to
get_expr()etc, these accessors only work on quosures and are
slightly more efficient. -
quo_is_symbol()andquo_is_call()now take the same set of
arguments asis_symbol()andis_call(). -
enquo()andenexpr()now deal with default values correctly (#201). -
Splicing a list no longer mutates it (#280).
Conditions
-
The new functions
cnd_warn()andcnd_inform()transform
conditions to warnings or messages before signalling them. -
cnd_signal()now returns invisibly. -
cnd_signal()andcnd_abort()now accept character vectors to
create typed conditions with several S3 subclasses. -
is_condition()is now properly exported. -
Condition signallers such as
cnd_signal()andabort()now accept
a call depth ascallarguments. This allows plucking a call from
further up the call stack (#30). -
New helper
catch_cnd(). This is a small wrapper around
tryCatch()that captures and returns any signalled condition. It
returnsNULLif none was signalled. -
cnd_abort()now adds the correct S3 classes for error
conditions. This fixes error catching, for instance by
testthat::expect_error().
Environments
-
env_get_list()retrieves muliple bindings from an environment into
a named list. -
with_bindings()andscoped_bindings()establish temporary
bindings in an environment. -
is_namespace()is a snake case wrapper aroundisNamespace().
Various features
-
New functions
inherits_any(),inherits_all(), and
inherits_only(). They allow testing for inheritance from multiple
classes. The_anyvariant is equivalent tobase::inherits()but
is more explicit about its behaviour.inherits_all()checks that
all classes are present in order andinherits_only()checks that
the class vectors are identical. -
New
fn_fmls<-andfn_fmls_names<-setters. -
New function experimental function
chr_unserialise_unicode()for
turning characters serialised to unicode point form
(e.g.<U+xxxx>) to UTF-8. In addition,as_utf8_character()now
translates those as well. (@krlmlr) -
expr_label()now supports quoted function definition calls (#275). -
call_modify()andcall_standardise()gain an argument to specify
an environment. The call definition is looked up in that environment
when the call to modify or standardise is not wrapped in a quosure. -
is_symbol()gains anameargument to check that that the symbol
name matches a string (#287). -
New
rlang_boxclass. Its purpose is similar to theAsIsclass
frombase::I(), i.e. it protects a value temporarily. However it
does so by wrapping the value in a scalar list. Usenew_box()to
create a boxed value,is_box()to test for a boxed value, and
unbox()to unbox it.new_box()andis_box()accept optional
subclass. -
The vector constructors such as
new_integer(),
new_double_along()etc gain anamesargument. In the case of the
_alongfamily it defaults to the names of the input vector.
Bugfixes
- When nested quosures are evaluated with
eval_tidy(), the.env
pronoun now correctly refers to the current quosure under evaluation
(#174). Previously it would always refer to the environment of the
outermost...
rlang 0.1.6
This is a maintenance release to anticipate the deprecation of
SET_NAMED() in the next version of R.
rlang 0.1.4
eval_tidy()no longer maps over lists but returns them literally.
This behaviour is an overlook from past refactorings and was never
documented.
rlang 0.1.2
This hotfix release makes rlang compatible with the R 3.1 branch.
rlang 0.1.1
Tidy evaluation fixes
This release includes two important fixes for tidy evaluation:
-
Bare formulas are now evaluated in the correct environment in
tidyeval functions. -
enquo()now works properly within compiled functions. Before this
release, constants optimised by the bytecode compiler couldn't be
enquoted.
New functions:
-
The
new_environment()constructor creates a child of the empty
environment and takes an optional named list of data to populate it.
Compared toenv()andchild_env(), it is meant to create
environments as data structures rather than as part of a scope
hierarchy. -
The
new_language()constructor creates calls out of a callable
object (a function or an expression) and a pairlist of arguments. It
is useful to avoid costly internal coercions between lists and
pairlists of arguments.
UI improvements:
-
env_child()'s first argument is now.parentinstead ofparent. -
mut_setters likemut_attrs()and environment helpers like
env_bind()andenv_unbind()now return their (modified) input
invisibly. This follows the tidyverse convention that functions
called primarily for their side effects should return their input
invisibly. -
is_pairlist()now returnsTRUEforNULL. We addedis_node()
to test for actual pairlist nodes. In other words,is_pairlist()
tests for the data structure whileis_node()tests for the type.
Bugfixes:
-
env()andenv_child()can now get arguments whose names start
with.. Prior to this fix, these arguments were partial-matching
onenv_bind()'s.envargument. -
The internal
replace_na()symbol was renamed to avoid a collision
with an exported function in tidyverse. This solves an issue
occurring in old versions of R prior to 3.3.2 (#133).
rlang 0.1.0
Initial release