6

R is giving the following message error when you want to save an S4 object into a list of list and the element was not already defined previously.

"invalid type/length (S4/0) in vector allocation"

Why is it working with a simple list, but not with a list of list?

See the following code and the potential workarounds. However, I am pretty sure there is a more obvious solution.

# Creation of an S4 object
setClass("student", slots=list(name="character", age="numeric", GPA="numeric"))
s <- new("student",name="John", age=21, GPA=3.5)

# Indexes for the list
index1 <- "A"
index2 <- "a"

# Simple list (All of this works)
l <- list()
l[[index1]] <- s
l[[index1]] <- "character"
l[[index1]] <- 999


# List of list 
l <- list()
l[[index1]][[index2]] <- s          # will give an Error!!
l[[index1]][[index2]] <- "character" # still working
l[[index1]][[index2]] <- 999         # still working


# "Workarounds"
l <- list()
l[[index1]][[index2]] <- rep(999, length(slotNames(s))) #define the element with a length equal to the number of slots in the s4 object
l[[index1]][[index2]] <- s # this works now!


l[[index1]][[index2]] <- list(s) # This works too, but that's not the same result

Any suggestion on why it does not work with a list of list and how I can solve this problem? Thanks

Arnaud
  • 377
  • 1
  • 2
  • 11

1 Answers1

5

So when you do

l <- list()
l[[index1]][[index2]] <- s

the problem is that that l is initialized to be a list so it makes sense to set a new named element with l[[index1]], but R has no idea what's stored at l[[index1]][[index2]]. It could be anything. It could be a function and functions don't know what to do with a named indexing operation. For example

l <- list()
l[[index1]] <- mean
l[[index1]][[index2]] <- "character"

But in your case, when you try to grab a value form a list that hasn't been initialized yet, you'll get NULL. For example

l <- list()
l[[index1]]
# NULL

R happens to have special behavior when you try set a named atomic value on a NULL object. Observe

# NULL[["a"]] <- "character" is basically calling....
`[[<-`(NULL, "a", "character")
#           a 
# "character" 

Note that we are getting a named vector here. Not a list. This is true for your "working" examples as well

l <- list()
l[[index1]][[index2]] <- "character"
class(l[[index1]][[index2]])
# [1] "character"

Also note that this doesn't have anything to do with S4 specifically. The same would happen if we tried to set a more complex objected like a function as well

l <- list()
l[[index1]][[index2]] <- mean
# Error in l[[index1]][[index2]] <- mean : 
#   invalid type/length (closure/0) in vector allocation

In languages like Perl you can "magically" bring hashes to life with the correct indexing syntax via autovivification, but that's not true in R. If you want a list() to exist at l[[index1]] you will need to explicitly create it. This will work

l <- list()
l[[index1]] <- list()
l[[index1]][[index2]] <- s

Again this is because [[ ]] is a bit ambiguous in R. It's a generic indexing function not used exclusively with lists.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • Thanks for the explanation ! – Arnaud Mar 25 '19 at 17:05
  • @MrFlick: so `l[[index1]][[index2]] <- s` tries to assign a name to the list and convert it into a vector (?) and it fails? I read through your explanation several times, but I'm still confused. – Dunois Mar 25 '19 at 17:17
  • 1
    @Dunois With `l[[index1]][[index2]] <- s` you are first calling `l[[index1]]` which returns NULL so you are basically (but not exactly) doing `NULL[[index2]] <- s` so you are trying to set a named element on a NULL object. For some reason R will allow this when `s` is a basic atomic type like a string or number and it interprets that as creating a named vector (not a list). But really it's not obvious what should happen there so an error makes the most sense to me. – MrFlick Mar 25 '19 at 17:22
  • @MrFlick: ah alright, that makes a lot more sense. I was lost between the "l[[index1]] which returns NULL" and the "NULL[[index2]]<-s" part of your explanation, and I could quite make the connection. Many thanks! (Plus maybe add this as a tl;dr to your post?) – Dunois Mar 25 '19 at 17:24