Skip to content

Releases: r-lib/rlang

rlang 0.3.0

23 Oct 13:51

Choose a tag to compare

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() and quo_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 than quo_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 to quos(x, y).

  • The sentinel for removing arguments in call_modify() has been
    changed from NULL to zap(). This breaking change is motivated
    by the ambiguity of NULL 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 with abort() 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 with trace_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 a parent 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() or rlang::with_handlers() and rethrow them with
    a high-level message. Call abort() with the intercepted error as
    the parent argument. When the user prints rlang::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 with abort(). When that's not the case, you can call the
    low-level function within with_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 that var 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
    calls as.list() on S3 objects and as(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 a parent 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 with rlang::last_error(). The error prints with a
    simplified backtrace. Call summary(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 the abort(), 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 function Rf_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() and inform() 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 to tryCatch()). 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 with list2()
    and in quoting functions. !!! accepts these types:

    • Lists, pairlists, and atomic vectors. If they have a class, they
      are converted with base::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() and enquos() 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() and enquos() now support .ignore_empty = "all"
    with named arguments as well (#414).

  • dots_list() gains a .preserve_empty argument. When TRUE, empty
    arguments are stored as missing arguments (see ?missing_arg).

  • dots_list(), enexprs() and enquos() gain a .check_assign
    argument. When TRUE, 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 evaluates foo (#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 roundtrip parse_expr(expr_deparse(x)) is not
    lossy.

  • new_data_mask() now throws an error when bottom is not a child
    of top (#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() and as_quosures() to create or
    coerce to a list of quosures. This is a small S3 class that ens...

Read more

rlang 0.2.2

19 Aug 16:30

Choose a tag to compare

This is a maintenance release that fixes several garbage collection
protection issues.

rlang 0.2.1

16 Jul 12:30

Choose a tag to compare

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

20 Feb 14:02

Choose a tag to compare

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 > b only unquotes a
    rather than the whole a > b expression.

  • enquo() works in magrittr pipes: mtcars %>% select(!!enquo(var)).

  • enquos() is a variant of quos() 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() and enquo() more efficient as well.

  • The tidy eval !! operator now binds tightly. You no longer have to
    wrap it in parentheses, i.e. !!x > y will only unquote x.

    Technically the !! operator has the same precedence as unary -
    and +. This means that !!a:b and !!a + b are equivalent to
    (!!a):b and (!!a) + b. On the other hand !!a^b and !!a$b are
    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
    than 1:2); unquoted S3 objects are displayed using
    pillar::type_sum() if available.

  • New enquos() function to capture arguments. It treats ... the
    same way as quos() but can also capture named arguments just like
    enquo(), i.e. one level up. By comparison quos(arg) only
    captures the name arg rather than the expression supplied to the
    arg argument.

    In addition, enexprs() is like enquos() but like exprs() it
    returns bare expressions. And ensyms() 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 to quo_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 of enexpr() that expects a symbol or a
    string and always returns a symbol. If a complex expression is
    supplied it fails with an error.

  • exprs() and quos() gain a .unquote_names arguments 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 .named option 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
    not dots_list(nested_call(!!! x)) (#217).

  • Functions taking dots with list2() or dots_list() now support
    splicing of NULL values. !!! NULL is 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 to lapply() 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() and UQS(). 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(), and quo_set_env(). Compared to
    get_expr() etc, these accessors only work on quosures and are
    slightly more efficient.

  • quo_is_symbol() and quo_is_call() now take the same set of
    arguments as is_symbol() and is_call().

  • enquo() and enexpr() now deal with default values correctly (#201).

  • Splicing a list no longer mutates it (#280).

Conditions

  • The new functions cnd_warn() and cnd_inform() transform
    conditions to warnings or messages before signalling them.

  • cnd_signal() now returns invisibly.

  • cnd_signal() and cnd_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() and abort() now accept
    a call depth as call arguments. 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
    returns NULL if 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() and scoped_bindings() establish temporary
    bindings in an environment.

  • is_namespace() is a snake case wrapper around isNamespace().

Various features

  • New functions inherits_any(), inherits_all(), and
    inherits_only(). They allow testing for inheritance from multiple
    classes. The _any variant is equivalent to base::inherits() but
    is more explicit about its behaviour. inherits_all() checks that
    all classes are present in order and inherits_only() checks that
    the class vectors are identical.

  • New fn_fmls<- and fn_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() and call_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 a name argument to check that that the symbol
    name matches a string (#287).

  • New rlang_box class. Its purpose is similar to the AsIs class
    from base::I(), i.e. it protects a value temporarily. However it
    does so by wrapping the value in a scalar list. Use new_box() to
    create a boxed value, is_box() to test for a boxed value, and
    unbox() to unbox it. new_box() and is_box() accept optional
    subclass.

  • The vector constructors such as new_integer(),
    new_double_along() etc gain a names argument. In the case of the
    _along family 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...
Read more

rlang 0.1.6

26 Dec 16:31

Choose a tag to compare

This is a maintenance release to anticipate the deprecation of
SET_NAMED() in the next version of R.

rlang 0.1.4

06 Nov 13:07
37e0215

Choose a tag to compare

  • 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

11 Aug 08:02

Choose a tag to compare

This hotfix release makes rlang compatible with the R 3.1 branch.

rlang 0.1.1

08 May 08:51

Choose a tag to compare

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 to env() and child_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 .parent instead of parent.

  • mut_ setters like mut_attrs() and environment helpers like
    env_bind() and env_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 returns TRUE for NULL. We added is_node()
    to test for actual pairlist nodes. In other words, is_pairlist()
    tests for the data structure while is_node() tests for the type.

Bugfixes:

  • env() and env_child() can now get arguments whose names start
    with .. Prior to this fix, these arguments were partial-matching
    on env_bind()'s .env argument.

  • 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

18 May 08:57

Choose a tag to compare

Initial release