r-lib / vctrs

Generic programming with typed R vectors
https://vctrs.r-lib.org
Other
287 stars 66 forks source link

Difference between `[<-.vctrs_list_of` and `vec_assign()` #1424

Open DavisVaughan opened 3 years ago

DavisVaughan commented 3 years ago

Why can we do the first, but not the second?

library(vctrs)

x <- list_of(1L, .ptype = integer())

x[1] <- list(2L)
x
#> <list_of<integer>[1]>
#> [[1]]
#> [1] 2

vec_assign(x, 1, list(2L))
#> Error: Can't convert <list> to <list_of<integer>>.

I think this is because there is no vec_cast.vctrs_list_of.list method, so then [<-.vctrs_list_of tries to make up for this by being a bit more lenient:

vctrs:::`[<-.vctrs_list_of`
#> function (x, i, value) 
#> {
#>     wrapped_type <- attr(x, "ptype")
#>     value <- map(value, vec_cast, to = wrapped_type)
#>     value <- new_list_of(value, ptype = attr(x, "ptype"))
#>     NextMethod()
#> }
#> <bytecode: 0x7f887144ba80>
#> <environment: namespace:vctrs>

The logic here is really what would be in a vec_cast.vctrs_list_of.list method.

DavisVaughan commented 3 years ago

I think this would be solved by https://github.com/r-lib/vctrs/pull/1231, which adds that missing cast method

But note that [<-.vctrs_list_of should still be updated or possibly removed entirely

lionel- commented 2 years ago

Ah we're already lenient. Then I'm not sure we should make this stricter.

DavisVaughan commented 2 years ago

You think [<- and vec_assign() should have different behavior? i.e. this looks right to you?

library(vctrs)

x <- list_of(1L, .ptype = integer())

x[1] <- 2
x
#> <list_of<integer>[1]>
#> [[1]]
#> [1] 2

vec_assign(x, 1, 3)
#> Error in `vec_assign()`:
#> ! Can't convert <double> to <list_of<integer>>.

Created on 2022-10-03 by the reprex package (v2.0.1)

lionel- commented 2 years ago

Possibly, because [<- is higher-level than vec_assign(). The latter is commonly called after types have been straightened out, unlike the former which is exposed to the user.

The downside of making vec_assign() behave like [<- is that it's only really possible to handle coercions at the R level. At the C level, that would require a costly indirection around vector storage (for proxy, array pointer, and restore ptype).