Skip to content

purrr 1.0.0

Compare
Choose a tag to compare
@hadley hadley released this 20 Dec 16:59

Breaking changes

Core purpose refinements

  • cross() and all its variants have been deprecated in favour of
    tidyr::expand_grid(). These functions were slow and buggy and we
    no longer think they are the right approach to solving this problem.
    See #768 for more information.

  • update_list() (#858) and rerun() (#877), and the use of tidyselect
    with map_at() and friends (#874) have been deprecated. These functions
    use some form of non-standard evaluation which we now believe is a poor
    fit for purrr.

  • The lift_* family of functions has been deprecated. We no longer believe
    these to be a good fit for purrr because they rely on a style of function
    manipulation that is very uncommon in R code (#871).

  • prepend(), rdunif(), rbernoulli(), when(), and list_along() have
    all been deprecated (#925). It's now clear that they don't align with the
    core purpose of purrr.

  • splice() is deprecated because we no longer believe that automatic
    splicing makes for good UI. Instead use list2() + !!! or
    list_flatten() (#869).

Mapping

  • Use of map functions with expressions, calls, and pairlists has been
    deprecated (#961).

  • All map _raw() variants have been deprecated because they are of limited
    use and you can now use map_vec() instead (#903).

  • In map_chr(), automatic conversion from logical, integer, and double to
    character is now deprecated. Use an explicit as.character() if needed
    (#904).

Deprecation next steps

  • as_function() and the ...f argument to partial() are no longer
    supported. They have been defunct for quite some time.

  • Soft deprecated functions: %@%, reduce_right(), reduce2_right(),
    accumulate_right() are now fully deprecated. Similarly, the
    .lazy, .env, and .first arguments to partial(),
    and the .right argument to detect() and detect_index()
    are fully deprecated. Removing elements with NULL in list_modify() and
    list_merge() is now fully deprecated.

  • is_numeric() and is_scalar_numeric() have been removed. They have
    been deprecated since purrr 0.2.3 (Sep 2017).

  • invoke_*() is now deprecated. It was superseded in 0.3.0 (Jan 2019) and
    3.5 years later, we have decided to deprecate it as part of the API
    refinement in the 1.0.0 release.

  • map_call() has been removed. It was made defunct in 0.3.0 (Jan 2019).

New features

  • *_at() can now take a function (or formula) that's passed the vector of
    element names and returns the elements to select.

  • New map_vec(), map2_vec(), and pmap_vec() work on all types of vectors,
    extending map_lgl(), map_int(), and friends so that you can easily work
    with dates, factors, date-times and more (#435).

  • New keep_at() and discard_at() that work like keep() and discard()
    but operation on element names rather than element contents (#817).

  • Some mapping functions have now a .progress argument to create a
    progress bar. See ?progress_bars (#149).

  • purrr is now licensed as MIT (#805).

  • modify(), modify_if(), modify_at(), and modify2() are no longer
    generics. We have discovered a simple implementation that no longer requires
    genericity and methods were only provided by a very small number of packages
    (#894).

  • purrr now uses the base pipe (|>) and anonymous function short hand (\(x)),
    in all examples. This means that examples will no longer work in R 4.0 and
    earlier so in those versions of R, the examples are automatically converted
    to a regular section with a note that they might not work (#936).

  • When map functions fail, they now report the element they failed at (#945).

  • New modify_tree() for recursively modifying nested data structures (#720).

Flattening and simplification

  • New list_c(), list_rbind(), and list_cbind() make it easy to
    c(), rbind(), or cbind() all of the elements in a list.

  • New list_simplify() reduces a list of length-1 vectors to a simpler atomic
    or S3 vector (#900).

  • New list_transpose() which automatically simplifies if possible (#875).

  • accumulate() and accumulate2() now both simplify the output if possible
    using vctrs. New arguments simplify and ptype allow you to control the
    details of simplification (#774, #809).

  • flatten() and friends are superseded in favour of list_flatten(),
    list_c(), list_cbind(), and list_rbind().

  • *_dfc() and *_dfr() have been superseded in favour of using the
    appropriate map function along with list_rbind() or list_cbind() (#912).

  • simplify(), simplify_all(), and as_vector() have been superseded in
    favour of list_simplify(). It provides a more consistent definition of
    simplification (#900).

  • transpose() has been superseded in favour of list_transpose() (#875).
    It has built-in simplification.

Tidyverse consistency

  • _lgl(), _int(), _int(), and _dbl() now use the same (strict) coercion
    methods as vctrs (#904). This means that:

    • map_chr(TRUE, identity), map_chr(0L, identity), and
      map_chr(1L, identity) are deprecated because we now believe that
      converting a logical/integer/double to a character vector should require
      an explicit coercion.

    • map_int(1.5, identity) now fails because we believe that silently
      truncating doubles to integers is dangerous. But note that
      map_int(1, identity) still works since no numeric precision is lost.

    • map_int(c(TRUE, FALSE), identity), map_dbl(c(TRUE, FALSE), identity),
      map_lgl(c(1L, 0L), identity) and map_lgl(c(1, 0), identity) now
      succeed because 1/TRUE and 0/FALSE should be interchangeable.

  • map2(), modify2(), and pmap() now use tidyverse recycling rules where
    vectors of length 1 are recycled to any size but all others must have
    the same length (#878).

  • map2() and pmap() now recycle names of their first input if
    needed (#783).

  • modify(), modify_if(), and modify_at() have been reimplemented using
    vctrs principles. This shouldn't have an user facing impact, but it does
    make the implementation much simpler.

Plucking

  • vec_depth() is now pluck_depth() and works with more types of input
    (#818).

  • pluck() now requires indices to be length 1 (#813). It also now reports
    the correct type if you supply an unexpected index.

  • pluck() now accepts negative integers, indexing from the right (#603).

  • pluck() and chuck() now fail if you provide named inputs to ... (#788).

  • pluck() no longer replaces 0-length vectors with default; it now
    only applies absent and NULL components (#480).

  • pluck<-/assign_in() can now modify non-existing locations (#704).

Setting with NULL

  • pluck<-/assign_in() now sets elements to NULL rather than removing them
    (#636). Now use the explicit zap() if you want to remove elements.

  • modify(), modify2(), and modify_if() now correctly handle NULLs
    in replacement values (#655, #746, #753).

  • list_modify()'s interface has been standardised. Modifying with NULL
    now always creates a NULL in the output (#810)

list_ functions`

  • New list_assign() which is similar to list_modify() but doesn't work
    recursively (#822).

  • list_modify() no longer recurses into data frames (and other objects built
    on top of lists that are fundamentally non-list like) (#810). You can
    revert to the previous behaviour by setting .is_node = is.list.

Minor improvements and bug fixes

  • capture_output() correctly uses conditionMessage() instead of directly
    interrogating the message field (#1010).

  • modify() no longer works with calls or pairlists.

  • modify_depth() is no longer a generic. This makes it more consistent
    with map_depth().

  • map_depth() and modify_depth() have a new is_node argument that
    allows you to control what counts as a level. The default uses
    vec_is_list() to avoid recursing into rich S3 objects like linear models
    or data.frames (#958, #920).

  • map_depth() and modify_depth() now correctly recurse at depth 1.

  • as_mapper() is now around twice as fast when used with character,
    integer, or list (#820).

  • possibly() now defaults otherwise to NULL.

  • modify_if(.else) is now actually evaluated for atomic vectors (@mgirlich,
    #701).

  • lmap_if() correctly handles .else functions (#847).

  • every() now correctly propagates missing values using the same
    rules as && (#751). Internally, it has become a wrapper around
    &&. This makes it consistent with && and also with some()
    which has always been a wrapper around || with the same
    propagation rules.

  • every() and some() now properly check the return value of their
    predicate function. It must now return a TRUE, FALSE, or NA.

  • Greatly improved performance of functions created with partial() (#715).
    Their invocation is now as fast as for functions creating manually.

  • partial() no longer inlines the function in the call stack. This
    fixes issues when partial() is used with lm() for instance (#707).