Releases: r-lib/rlang
rlang 0.4.4
- Maintenance release for CRAN.
rlang 0.4.3
-
You can now use glue syntax to unquote on the LHS of
:=
. This
syntax is automatically available in all functions taking dots with
list2()
andenquos()
, and thus most of the tidyverse. Note that
if you use the glue syntax in an R package, you need to import glue.A single pair of braces triggers normal glue interpolation:
df <- data.frame(x = 1:3) suffix <- "foo" df %>% dplyr::mutate("var_{suffix}" := x * 2) #> x var_foo #> 1 1 2 #> 2 2 4 #> 3 3 6
Using a pair of double braces is for labelling a function argument.
Technically, this is shortcut for"{as_label(enquo(arg))}"
. The
syntax is similar to the curly-curly syntax for interpolating
function arguments:my_wrapper <- function(data, var, suffix = "foo") { data %>% dplyr::mutate("{{ var }}_{suffix}" := {{ var }} * 2) } df %>% my_wrapper(x) #> x x_foo #> 1 1 2 #> 2 2 4 #> 3 3 6 df %>% my_wrapper(sqrt(x)) #> x sqrt(x)_foo #> 1 1 2.000000 #> 2 2 2.828427 #> 3 3 3.464102
-
Fixed a bug in magrittr backtraces that caused duplicate calls to
appear in the trace. -
Fixed a bug in magrittr backtraces that caused wrong call indices.
-
Empty backtraces are no longer shown when
rlang_backtrace_on_error
is set. -
The tidy eval
.env
pronoun is now exported for documentation
purposes. -
warn()
andabort()
now check that eitherclass
ormessage
was supplied.inform()
allows sending empty message as it is
occasionally useful for building user output incrementally. -
flatten()
fails with a proper error when input can't be flattened (#868, #885). -
inform()
now consistently appends a final newline to the message
(#880). -
cnd_body.default()
is now properly registered. -
cnd_signal()
now uses the same approach asabort()
to save
unhandled errors tolast_error()
. -
Parsable constants like
NaN
andNA_integer_
are now deparsed by
expr_deparse()
in their parsable form (#890). -
Infix operators now stick to their LHS when deparsed by
expr_deparse()
(#890).
rlang 0.4.2
-
New
cnd_header()
,cnd_body()
andcnd_footer()
generics. These
are automatically called byconditionMessage.rlang_error()
, the
default method for all rlang errors.Concretely, this is a way of breaking up lazy generation of error
messages withconditionMessage()
into three independent
parts. This provides a lot of flexibility for hierarchies of error
classes, for instance you could inherit the body of an error message
from a parent class while overriding the header and footer. -
The reminder to call
last_error()
is now less confusing thanks to
a suggestion by @markhwhiteii. -
The functions prefixed in
scoped_
have been renamed to use the
more conventionallocal_
prefix. For instance,scoped_bindings()
is nowlocal_bindings()
. Thescoped_
functions will be
deprecated in the next significant version of rlang (0.5.0). -
The
.subclass
argument ofabort()
,warn()
andinform()
has
been renamed toclass
. This is for consistency with our
conventions for class constructors documented in
https://adv-r.hadley.nz/s3.html#s3-subclassing. -
inform()
now prints messages to the standard output by default in
interactive sessions. This makes them appear more like normal output
in IDEs such as RStudio. In non-interactive sessions, messages are
still printed to standard error to make it easy to redirect messages
when running R scripts (#852). -
Fixed an error in
trace_back()
when the call stack contains a
quosured symbol. -
Backtrace is now displayed in full when an error occurs in
non-interactive sessions. Previously the backtraces of parent errors
were left out.
rlang 0.4.1
-
New experimental framework for creating bulleted error messages. See
?cnd_message
for the motivation and an overwiew of the tools we
have created to support this approach. In particular,abort()
now
takes character vectors to assemble a bullet list. Elements named
x
are prefixed with a red cross, elements namedi
are prefixed
with a blue info symbol, and unnamed elements are prefixed with a
bullet. -
Capture of backtrace in the context of rethrowing an error from an
exiting handler has been improved. ThetryCatch()
context no
longer leaks in the high-level backtrace. -
Printing an error no longer recommends calling
last_trace()
,
unless called fromlast_error()
. -
env_clone()
no longer recreates active bindings and is now just an
alias forenv2list(as.list(env))
. Unlikeas.list()
which returns
the active binding function on R < 4.0, the value of active bindings
is consistently used in all versions. -
The display of rlang errors derived from parent errors has been
improved. The simplified backtrace (as printed by
rlang::last_error()
) no longer includes the parent errors. On the
other hand, the full backtrace (as printed byrlang::last_trace()
)
now includes the backtraces of the parent errors. -
cnd_signal()
has improved support for rlang errors created with
error_cnd()
. It now records a backtrace if there isn't one
already, and saves the error so it can be inspected with
rlang::last_error()
. -
rlang errors are no longer formatted and saved through
conditionMessage()
. This makes it easier to use a
conditionMessage()
method in subclasses created withabort()
,
which is useful to delay expensive generation of error messages
until display time. -
abort()
can now be called without error message. This is useful
whenconditionMessage()
is used to generate the message at
print-time. -
Fixed an infinite loop in
eval_tidy()
. It occurred when evaluating
a quosure that inherits from the mask itself. -
env_bind()
's performance has been significantly improved by fixing a bug
that caused values to be repeatedly looked up by name. -
cnd_muffle()
now checks that a restart exists before invoking
it. The restart might not exist if the condition is signalled with a
different function (such asstop(warning_cnd)
). -
trace_length()
returns the number of frames in a backtrace. -
Added internal utility
cnd_entrace()
to add a backtrace to a
condition. -
rlang::last_error()
backtraces are no longer displayed in red. -
x %|% y
now also works wheny
is of same length asx
(@rcannood, #806). -
Empty named lists are now deparsed more explicitly as
"<named list>"
. -
Fixed
chr()
bug causing it to return invisibly.
rlang 0.4.0
Tidy evaluation
Interpolate function inputs with the curly-curly operator
The main change of this release is the new tidy evaluation operator
{{
. This operator abstracts the quote-and-unquote idiom into a
single interpolation step:
my_wrapper <- function(data, var, by) {
data %>%
group_by({{ by }}) %>%
summarise(average = mean({{ var }}, na.rm = TRUE))
}
{{ var }}
is a shortcut for !!enquo(var)
that should be easier on
the eyes, and easier to learn and teach.
Note that for multiple inputs, the existing documentation doesn't
stress enough that you can just pass dots straight to other tidy eval
functions. There is no need for quote-and-unquote unless you need to
modify the inputs or their names in some way:
my_wrapper <- function(data, var, ...) {
data %>%
group_by(...) %>%
summarise(average = mean({{ var }}, na.rm = TRUE))
}
More robust .env
pronoun
Another improvement to tidy evaluation should make it easier to use
the .env
pronoun. Starting from this release, subsetting an object
from the .env
pronoun now evaluates the corresponding symbol. This
makes .env
more robust, in particular in magrittr pipelines. The
following example would previously fail:
foo <- 10
mtcars %>% mutate(cyl = cyl * .env$foo)
This way, using the .env
pronoun is now equivalent to unquoting a
constant objects, but with an easier syntax:
mtcars %>% mutate(cyl = cyl * !!foo)
Note that following this change, and despite its name, .env
is no
longer referring to a bare environment. Instead, it is a special
shortcut with its own rules. Similarly, the .data
pronoun is not
really a data frame.
New functions and features
-
New
pairlist2()
function with splicing support. It preserves
missing arguments, which makes it useful for lists of formal
parameters for functions. -
is_bool()
is a scalar type predicate that checks whether its input
is a singleTRUE
orFALSE
. Likeis_string()
, it returns
FALSE
when the input is missing. This is useful for type-checking
function arguments (#695). -
is_string()
gains astring
argument.is_string(x, "foo")
is a
shortcut foris_character(x) && length(x) == 1 && identical(x, "foo")
. -
Lists of quosures now have pillar methods for display in tibbles.
-
set_names()
now names unnamed input vectors before applying a
function. The following expressions are now equivalent:letters %>% set_names() %>% set_names(toupper) letters %>% set_names(toupper)
-
You can now pass a character vector as message argument for
abort()
,warn()
,inform()
, andsignal()
. The vector is
collapsed to a single string with a"\n"
newline separating each
element of the input vector (#744). -
maybe_missing()
gains adefault
argument. -
New functions for weak references:
new_weakref()
,weakref_key()
,
weakref_value()
, andis_weakref()
(@wch, #787).
Performance
-
The performance of
exec()
has been improved. It is now on the same
order of performance asdo.call()
, though slightly slower. -
call2()
now uses the newpairlist2()
function internally. This
considerably improves its performance. This also means it now
preserves empty arguments:call2("fn", 1, , foo = ) #> fn(1, , foo = )
Bugfixes and small improvements
-
with_handlers()
now installs calling handlers first on the stack,
no matter their location in the argument list. This way they always
take precedence over exiting handlers, which ensures their side
effects (such as logging) take place (#718). -
In rlang backtraces, the
global::
prefix is now only added when
the function directly inherits from the global environment.
Functions inheriting indirectly no longer have a namespace
qualifier (#733). -
options(error = rlang::entrace)
now has better support for errors
thrown from C (#779). It also saves structured errors in theerror
field ofrlang::last_error()
. -
ns_env()
andns_env_name()
(experimental functions) now support
functions and environments consisently. They also require an
argument from now on. -
is_interactive()
is aware of theTESTTHAT
environment variable and
returnsFALSE
when it is"true"
(@jennybc, #738). -
fn_fmls()
and variants no longer coerce their input to a
closure. Instead, they throw an error. -
Fixed an issue in knitr that caused backtraces to print even when
error = TRUE
. -
The return object from
as_function()
now inherits from
"function"
(@richierocks, #735).
Lifecycle
We commit to support 5 versions of R. As R 3.6 is about to be
released, rlang now requires R 3.2 or greater. We're also continuing
our efforts to streamline and narrow the rlang API.
-
modify()
andprepend()
(two experimental functions marked as in
the questioning stage since rlang 0.3.0) are now deprecated. Vector
functions are now out of scope for rlang. They might be revived in
the vctrs or funs packages. -
exiting()
is soft-deprecated becausewith_handlers()
treats
handlers as exiting by default. -
The vector constructors like
lgl()
ornew_logical()
are now in
the questioning stage. They are likely to be moved to the vctrs
package at some point. Same for the missing values shortcuts like
na_lgl
. -
as_logical()
,as_integer()
, etc have been soft-deprecated in
favour ofvctrs::vec_cast()
. -
type_of()
,switch_type()
,coerce_type()
, and friends are
soft-deprecated. -
The encoding and locale API was summarily archived. This API didn't
bring any value and wasn't used on CRAN. -
lang_type_of()
,switch_lang()
, andcoerce_lang()
were
archived. These functions were not used on CRAN or internally. -
Subsetting quosures with
[
or[[
is soft-deprecated. -
All functions that were soft-deprecated, deprecated, or defunct in
previous releases have been bumped to the next lifecycle stage.
rlang 0.3.4
-
Fixed a unit test that failed on the Solaris CRAN machine.
-
Fixed a valgrind error (@BillDunlap).
rlang 0.3.3
-
Fixed an issue in knitr that caused backtraces to print even when
error = TRUE
. -
maybe_missing()
gains adefault
argument.
rlang 0.3.2
-
Fixed protection issue reported by rchk.
-
The experimental option
rlang__backtrace_on_error
is no longer
experimental and has been renamed torlang_backtrace_on_error
. -
New "none" option for
rlang_backtrace_on_error
. -
Unary operators applied to quosures now give better error messages.
-
Fixed issue with backtraces of warnings promoted to error, and
entraced viawithCallingHandlers()
. The issue didn't affect
entracing via top leveloptions(error = rlang::entrace)
handling.
rlang 0.3.1
This patch release polishes the new backtrace feature introduced in
rlang 0.3.0 and solves bugs for the upcoming release of purrr
0.3.0. It also features as_label()
and as_name()
which are meant
to replace quo_name()
in the future. Finally, a bunch of deparsing
issues have been fixed.
Backtrace fixes
-
New
entrace()
condition handler. Add this to your RProfile to
enable rlang backtraces for all errors, including warnings promoted
to errors:if (requireNamespace("rlang", quietly = TRUE)) { options(error = rlang::entrace) }
This handler also works as a calling handler:
with_handlers( error = calling(entrace), foo(bar) )
However it's often more practical to use
with_abort()
in that case:with_abort(foo(bar))
-
with_abort()
gains aclasses
argument to promote any kind of
condition to an rlang error. -
New
last_trace()
shortcut to print the backtrace stored in the
last_error()
. -
Backtrace objects now print in full by default.
-
Calls in backtraces are now numbered according to their position in
the call tree. The numbering is non-contiguous for simplified
backtraces because of omitted call frames. -
catch_cnd()
gains aclasses
argument to specify which classes of
condition to catch. It returnsNULL
if the expected condition
could not be caught (#696).
as_label()
and as_name()
The new as_label()
and as_name()
functions should be used instead
of quo_name()
to transform objects and quoted expressions to a
string. We have noticed that tidy eval users often use quo_name()
to
extract names from quosured symbols. This is not a good use for that
function because the way quo_name()
creates a string is not a well
defined operation.
For this reason, we are replacing quo_name()
with two new functions
that have more clearly defined purposes, and hopefully better names
reflecting those purposes. Use as_label()
to transform any object to
a short human-readable description, and as_name()
to extract names
from (possibly quosured) symbols.
Create labels with as_label()
to:
-
Display an object in a concise way, for example to labellise axes
in a graphical plot. -
Give default names to columns in a data frame. In this case,
labelling is the first step before name repair.
We expect as_label()
to gain additional parameters in the future,
for example to control the maximum width of a label. The way an object
is labelled is thus subject to change.
On the other hand, as_name()
transforms symbols back to a string in
a well defined manner. Unlike as_label()
, as_name()
guarantees the
roundtrip symbol -> string -> symbol.
In general, if you don't know for sure what kind of object you're
dealing with (a call, a symbol, an unquoted constant), use
as_label()
and make no assumption about the resulting string. If you
know you have a symbol and need the name of the object it refers to,
use as_name()
. For instance, use as_label()
with objects captured
with enquo()
and as_name()
with symbols captured with ensym()
.
Note that quo_name()
will only be soft-deprecated at the next major
version of rlang (0.4.0). At this point, it will start issuing
once-per-session warnings in scripts, but not in packages. It will
then be deprecated in yet another major version, at which point it
will issue once-per-session warnings in packages as well. You thus
have plenty of time to change your code.
Minor fixes and features
-
New
is_interactive()
function. It serves the same purpose as
base::interactive()
but also checks if knitr is in progress and
provides an escape hatch. Usewith_interactive()
and
scoped_interactive()
to override the return value of
is_interactive()
. This is useful in unit tests or to manually turn
on interactive features in RMarkdown outputs -
calling()
now boxes its argument. -
New
done()
function to box a value. Done boxes are sentinels to
indicate early termination of a loop or computation. For instance,
it will be used in the purrr package to allow users to shortcircuit
a reduction or accumulation. -
new_box()
now accepts additional attributes passed tostructure()
. -
Fixed a quotation bug with binary operators of zero or one argument
such as`/`(1)
(#652). They are now deparsed and printed
properly as well. -
New
call_ns()
function to retrieve the namespace of a
call. ReturnsNULL
if the call is not namespaced. -
Top-level S3 objects are now deparsed properly.
-
Empty
{
blocks are now deparsed on the same line. -
Fixed a deparsing issue with symbols containing non-ASCII
characters (#691). -
expr_print()
now handles[
and[[
operators correctly, and
deparses non-syntactic symbols with backticks. -
call_modify()
now respects ordering of unnamed inputs. Before this
fix, it would move all unnamed inputs after named ones. -
as_closure()
wrappers now call primitives with positional
arguments to avoid edge case issues of argument matching. -
as_closure()
wrappers now dispatch properly on methods defined in
the global environment (tidyverse/purrr#459). -
as_closure()
now supports both base-style (e1
ande2
) and
purrr-style (.x
and.y
) arguments with binary primitives. -
exec()
takes.fn
as first argument instead off
, for
consistency with other rlang functions. -
Fixed infinite loop with quosures created inside a data mask.
-
Base errors set as
parent
of rlang errors are now printed
correctly.
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_text
and?quo_name
to
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 fromNULL
tozap()
. This breaking change is motivated
by the ambiguity ofNULL
with 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 aparent
argument. 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
theparent
argument. 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 thatvar
in.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 aparent
argument 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_error
to display
backtraces alongside error messages. See?rlang::abort
for
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_empty
argument. 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 whenbottom
is 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...