r-lib / rlang

Low-level API for programming with R
https://rlang.r-lib.org
Other
500 stars 137 forks source link

Deprecate flatten and squash functions #1576

Closed lionel- closed 1 year ago

lionel- commented 1 year ago

Branched from #1575.

Deprecate flatten(), squash(), and their variants in favour of purrr functions.

cc @hadley @DavisVaughan

echasnovski commented 1 year ago

@lionel- , sorry to bother here.

I've just encountered the soft deprecation of rlang::squash() which I heavily use in ruler. As far as I've researched, there is no immediate replacement for it in purrr (and deprecation note doesn't seem to suggest anything in particular). purrr::list_flatten() removes only one level while I need to convert whole nested structure into a single list. The only approach I can see is to repeatedly call purrr::list_flatten() to achieve same effect.

Could you, please, recommend where I'd better look to resolve the issue?

DavisVaughan commented 1 year ago

I did have to add this helper to dplyr to retain squash-like behavior

#' @param x A list
#' @param fn An optional function of 1 argument to be applied to each list
#'   element of `x`. This allows you to further refine what elements should be
#'   flattened. `fn` should return a single `TRUE` or `FALSE`.
#' @param recursive Should `list_flatten()` be applied recursively? If `TRUE`,
#'   it will continue to apply `list_flatten()` as long as at least one element
#'   of `x` was flattened in the previous iteration.
#' @noRd
list_flatten <- function(x, ..., fn = NULL, recursive = FALSE) {
  check_dots_empty0(...)

  obj_check_list(x)
  x <- unclass(x)

  loc <- map_lgl(x, obj_is_list)

  if (!is_null(fn)) {
    loc[loc] <- map_lgl(x[loc], fn)
  }

  not_loc <- !loc

  names <- names(x)
  if (!is_null(names)) {
    # Always prefer inner names, even if inner elements are actually unnamed.
    # This is what `rlang::flatten_if()` did, with a warning. We could also
    # use `name_spec` and `name_repair` for a more complete solution.
    names[loc] <- ""
    names(x) <- names
  }

  x[loc] <- map(x[loc], unclass)
  x[not_loc] <- map(x[not_loc], list)

  out <- list_unchop(x, ptype = list())

  if (recursive && any(loc)) {
    out <- list_flatten(out, fn = fn, recursive = TRUE)
  }

  out
}
echasnovski commented 1 year ago

I did have to add this helper to dplyr to retain squash-like behavior

Yeah, so it seems that writing own helper with iterative purrr::list_flatten() application is the way to go. Shame to have a deprecation without immediate alternative :(

hadley commented 1 year ago

@DavisVaughan can you add that code to a purrr issue? It feels like purrr is the right home for this sort of thing.