Breaking changes

purrr 1.1.0

purrr
There are four important changes in purrr 1.0.0.
Published

December 20, 2022

Install purrr 1.0.0 with:

pak::pak("cran/purrr@1.0.0")

Load the package with:

Breaking changes

There are four important changes in purrr 1.0.0:

  • pluck() behaves differently when extracting 0-length vectors.
  • The map() family uses the tidyverse rules for coercion and recycling.
  • All functions that modify lists handle NULL consistently.
  • Deprecated functions that aren’t related to the core purpose of purrr.

pluck() and zero-length vectors

pluck() is a function that lets you safely get or set an element within a nested structure.

Before, pluck() replaced 0-length vectors with the value of default. Now default is only used for NULL and absent elements:

library(purrr)

x <- list(y = list(a = character(), b = NULL))
x
$y
$y$a
character(0)

$y$b
NULL
library(purrr, lib.loc = new_lib)
x |> pluck("y", "a", .default = NA)
character(0)
library(purrr)
x |> pluck("y", "a", .default = NA)
character(0)
x |> pluck("y", "b", .default = NA)
[1] NA
x |> pluck("y", "c", .default = NA)
[1] NA

This also impacts map_* because using an integer vector, character vector, or list instead of a function automatically calls pluck():

x <- list(list(1), list(), list(NULL), list(character()))
x |> map(1, .default = 0) |> str()
List of 4
 $ : num 1
 $ : num 0
 $ : num 0
 $ : chr(0) 

Tidyverse consistency

The team edited the map_* family to be consistent with general tidyverse coercion and recycling rules, as implemented by the vctrs package.

map_chr(1:4, \(x) x + 1)
[1] "2.000000" "3.000000" "4.000000" "5.000000"
map_chr(1:4, \(x) as.character(x + 1))
[1] "2" "3" "4" "5"

General principles:

  • Converting a logical/integer/double to a character vector is potentially dangerous and should require an explicit coercion.
  • Vectors of length 1 are recycled to any size but all other vectors must have the same length.

Assigning NULL

purrr has a number of functions that modify a list:

Previously, these functions had inconsistent behavior when you attempted to modify an element with NULL: some functions would delete that element, and some would set it to NULL.

x1 <- x2 <- x3 <- list(a = 1, b = 2)

x1$a <- NULL
str(x1)
List of 1
 $ b: num 2
x2["a"] <- list(NULL)
str(x2)
List of 2
 $ a: NULL
 $ b: num 2

Now functions that edit a list will create an element containing NULL:

x3 |> 
  list_modify(a = NULL) |> 
  str()
List of 2
 $ a: NULL
 $ b: num 2
x3 |> 
  modify_at("b", \(x) NULL) |> 
  str()
List of 2
 $ a: num 1
 $ b: NULL

If you want to delete the element, you can use zap():

x3 |> 
  list_modify(a = zap()) |> 
  str()
List of 1
 $ b: num 2

Deprecations

cross() and cross_df()

cross()and all its variants have been deprecated in favor of tidyr::expand_grid(). These functions were slow and buggy:

k = 12
x = rep(list(1:3), k) |>
  setNames(LETTERS[1:k])

system.time({ x |> purrr::cross_df() })
   user  system elapsed 
 28.994   0.615  33.980 
system.time({ x |> tidyr::expand_grid() })
   user  system elapsed 
  0.111   0.006   0.118 

update_list(), rerun(), and the use of tidyselect with map_at()

update_list(), rerun(), and the use of tidyselect with map_at() and friends have been deprecated because non-standard evaluation is not a good fit for purrr.

1

In most programming languages, you can only access the values of a function’s arguments. In R, you can also access the code used to compute them. This makes it possible to evaluate code in non-standard ways: to use what is known as non-standard evaluation, or NSE for short.

lift_*

The lift_* family of functions has been superceded because they promote a style of function manipulation that is not commonly used in R.

prepend(), rdunif(), rbernoulli(), when(), and list_along()

These have been deprecated because they’re not directly related to functional programming.

splice()

splice() has been deprecated because automatic splicing doesn’t make for good UI and there are other ways to achieve the same result.

Learn more

Footnotes

  1. ↩︎

https://adv-r.hadley.nz/metaprogramming.html