purrr icon indicating copy to clipboard operation
purrr copied to clipboard

treat vctrs record rcrd as a list

Open maxheld83 opened this issue 4 years ago • 0 comments

A vctrs record type, while implemented as a list (of equal-lengths vectors) under the hood, is otherwise treated as "atomic" externally (loosely speaking). My naive expectation had been that purrr::map() would treat it thus and loop over it's length, not over the list of its internal structure.

library(vctrs)

new_rational <- function(n = integer(), d = integer()) {
  # assertions from vignette dropped
  new_rcrd(list(n = n, d = d), class = "vctrs_rational")
}

format.vctrs_rational <- function(x, ...) {
  n <- field(x, "n")
  d <- field(x, "d")

  out <- paste0(n, "/", d)
  out[is.na(n) | is.na(d)] <- NA

  out
}

x <- new_rational(c(1, 1, 1), c(2, 2, 2))
# vctrs API sugggests a record, externally viewed, is NOT a list
vctrs::vec_is_list(x)
#> [1] FALSE
length(x)
#> [1] 3
# base R, oddly, agrees
lapply(x, str)
#>  vctrs_rt [1:1] 1/2
#>  vctrs_rt [1:1] 1/2
#>  vctrs_rt [1:1] 1/2
#> [[1]]
#> <vctrs_rational[1]>
#> [1] 1/2
#> 
#> [[2]]
#> <vctrs_rational[1]>
#> [1] 1/2
#> 
#> [[3]]
#> <vctrs_rational[1]>
#> [1] 1/2
# but purrr treats a vctrs record as a list of *fields*
purrr::map(x, str)
#>  vctrs_rt [1:1] 1/2
#>  vctrs_rt [1:1] 1/2
#> $n
#> <vctrs_rational[1]>
#> [1] 1/2
#> 
#> $d
#> <vctrs_rational[1]>
#> [1] 1/2

Created on 2021-02-25 by the reprex package (v1.0.0)

My expectation had been that purrr::map() would not break the external appearance of records and that, had I needed to get into the fields, I would have had to do that myself:

map(x, function(i) {
  paste0(vctrs::field(i, "n"), vctrs::field(i, "d"))
})

Ps.: I've read many of the issues and lengthy discussions on map() and vctrs, and I hope this isn't a duplicate. I'm guessing https://github.com/r-lib/vctrs/issues/495 is the closest one, though that's about implementing vctrs::vec_map().

maxheld83 avatar Feb 24 '21 23:02 maxheld83