Skip to content

Should size 1 vectors be allowed to have "" as names? #780

Description

@DavisVaughan

We have a small inconsistency with this edge case. vec_slice(x, NA_integer_) can generate a named vector of size 1 with "" as names. But new_vctr() doesn't like this, as it errors if "all names are the empty string" (see names_all_or_nothing())

Should vec_slice() be tweaked to not restore any names for this edge case, or should names_all_or_nothing() be more relaxed?

I think tweaking vec_slice() is correct. I don't think we want vec_slice(c(x = 1), NA_integer_) to return a vector with any names.

library(vctrs)

# Is this okay for names?
x <- c(foo = 1)

vec_slice(x, NA_integer_)
#>    
#> NA

names(vec_slice(x, NA_integer_))
#> [1] ""

# We have an inconsistency because `vec_slice()` can generate these size 1
# vectors where "all names are the empty string", but that is not allowed by
# new_vctr()'s `names_all_or_nothing()` check
y <- vec_slice(x, NA_integer_)
new_vctr(y)
#> Error: If any elements of `.data` are named, all must be named

# This mainly causes issues if you have a restore method that might eventually
# call `new_vctr()`

z <- new_vctr(c(foo = 1), class = "foobar", inherit_base_type = FALSE)

# Fine because default restore doesn't go through `new_vctr()`
vec_slice(z, NA_integer_)
#> <foobar[1]>
#>    
#> NA

vec_restore.foobar <- function(x, to, ..., n = NULL) {
  new_vctr(x, class = "foobar", inherit_base_type = FALSE)
}

vec_slice(z, NA_integer_)
#> Error: If any elements of `.data` are named, all must be named

Created on 2020-01-27 by the reprex package (v0.3.0.9000)

Activity

  1. DavisVaughan commented on Jan 28, 2020

    @DavisVaughan
    MemberAuthor
    library(vctrs)
    
    z <- new_vctr(c(foo = 1), class = "foobar", inherit_base_type = FALSE)
    
    vec_slice(z, c(NA_integer_, NA_integer_))
    #> <foobar[2]>
    #>       
    #> NA NA
    
    vec_restore.foobar <- function(x, to, ..., n = NULL) {
      new_vctr(x, class = "foobar", inherit_base_type = FALSE)
    }
    
    vec_slice(z, c(NA_integer_, NA_integer_))
    #> Error: If any elements of `.data` are named, all must be named

    I guess this isn't specifically a size 1 thing. It happens any time you have a named vector and you slice all NA_integer_ elements

    Should having any "" names be allowed in names_all_or_nothing()?

    Actually I'm wondering if we need validate_names() at all? Maybe we should disallow NA_character_ names but otherwise allow anything else

    Maybe the rule for vec_slice() is "if all elements of i are NA_integer_, remove all names"

  2. lionel- commented on Feb 25, 2020

    @lionel-
    Member

    I don't think we want vec_slice(c(x = 1), NA_integer_) to return a vector with any names.

    I'm not so sure. It depends if you consider the presence of a character vector in the names attribute to be part of the type.

    Taking a step back, having NULL names for vectors is just an optimisation. In the ALTREP world this optimisation is no longer necessary. So returning NULL names instead of "" feels like a step in the wrong direction.

    I think the vctrs_vctr class should treat repetitions of "" in the same way as NULL names.

  3. DavisVaughan commented on Feb 25, 2020

    @DavisVaughan
    MemberAuthor

    I think the vctrs_vctr class should treat repetitions of "" in the same way as NULL names.

    @lionel- I think this is what I landed on too, and is what #784 does? The comments here got a little confusing as I was still trying to figure it out

  4. lionel- commented on Feb 25, 2020

    @lionel-
    Member

    Oh I didn't see the PR was more liberal. I think it's more liberal than the suggestion above because it allows names == c("", "a") instead of only allowing vectors of all empty names.

    I don't have a strong opinion about this. It forces the user to use a different default name for unnamed elements, which may make sense. @hadley should chime in as he implemented this behaviour.

  5. DavisVaughan commented on Feb 25, 2020

    @DavisVaughan
    MemberAuthor

    The quick rationale for allowing names == c("", "a") is because they can be generated by, say, vec_slice(z, c(NA_integer_, 1)), and if you have a restore method that goes through the new_vctr() constructor then you get stuck. Agreed about getting Hadley's input here!

  6. lionel- commented on Feb 25, 2020

    @lionel-
    Member

    That rationale makes sense to me. Actually that's probably the rationale for setting names to NA rather than "" here:

    set_names(letters)[c(1, NA)]
    #>    a <NA> 
    #>  "a"   NA 

    The names are really missing rather than non-existent.

  7. jennybc commented on Feb 25, 2020

    @jennybc
    Member

    I will say that one of the high-level impressions I got from my deep dive into names is that NA is the weirdest and most pathological option of all. FWIW here's some relevant text from ?names:

    The name "" is special: it is used to indicate that there is no name associated with an element of a (atomic or generic) vector. Subscripting by "" will match nothing (not even elements which have no name).

    A name can be character NA, but such a name will never be matched and is likely to lead to confusion.

    This is substantially why the "minimal" level of name repair forbids NAs.I realize that vctrs is at least partially motivated by the need to rethink and improve some fundamental stuff and maybe that applies here. But I'd carefully consider this before making intentional use of NA in names.

  8. lionel- commented on Feb 25, 2020

    @lionel-
    Member

    I really wasn't considering using NA, as it makes the types and hence the code more complex. Just providing some context on an old design decision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions