Brief background: Many (most?) modern programming languages in widespread use have at least a handful of ADTs in common, in particular, string (a (sequence comprised of characters), list (an ordered collection of values), and a map-based type (an unordered key-value store). In the R, the first two are implemented as 'character' and 'vector', respectively.
When i began learning R it seemed to me that R's 'List' is a straightforward implementation of the map ADT ('dictionary' in Python, 'NSMutableDictionary' in Objective C, 'hash' in Perl and so forth).
For instance, you create them just like you would a Python dictionary, by passing key-value pairs to a constructor (which in Python is 'dict' not 'list'):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
And you access the items of an R List just like you would those of a Python dictionary, e.g., x['ev1']. Likewise, you can retrieve just the 'keys' or just the 'values' by:
names(x)
# => "ev1" "ev2" "rv"
unlist(x)
# => 10 15 "Group1"
x = list("a"=6, "b"=9, "c"=3)
sum(unlist(x))
# => 18
But R Lists are also unlike other map-type ADTs (from among the languages i've learned anyway). My guess is that this is a consequence of the initial spec for S, i.e., an intention to design a data/statistics DSL from the ground-up. For one thing, and it's a big thing, Lists are an ordered collection, just like vectors, even though the values are keyed. I assume this choice came with a performance penalty.
For another, lists can be returned from functions even though you never passed in a List when you called the function, and even though the function doesn't contain a List constructor, e.g.,
x = strsplit(LETTERS[1:10], "") # passing in an object of type 'character'
class(x)
# => 'list'
Of course, you can deal with this in practice by wrapping the returned result in a call to 'unlist'.
Another peculiar feature of R's Lists: it doesn't seem that they can be members of another ADT, and if you try to do that then the primary container is coerced to a list. E.g.,
x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=T)
class(x)
# => 'list'
My intention here is not to criticize the language or how it's documented; likewise, i'm not suggesting there is anything wrong with the List ADT works. All i want to correct is my understanding of how they work so i can correctly use them in my code. Here are the sorts of things i'd like to better understand:
What are the rules which determine when a function call will return a List (e.g., 'strsplit' expression recited above)?
If i don't explicitly assign names to a list (e.g., list(10,20,30,40)) are the default names the indices? (I assume, but i am far from certain that the answer is yes, otherwise we wouldn't be able to coerce this type of List to a vector w/ a call to 'unlist'.
Why do these two calls return the same result:
x = list(1, 2, 3, 4)
x[1] # returns '1'
x[[1]] # returns '1'
Why do these two expressions not return the same result?
x = list(1, 2, 3, 4)
x2 = list(1:4)
Please don't point me to the R Documentation--i've read it and it doesn't work for me in this instance.
(There is a package available on CRAN called "hash" which does implements conventional map-type behavior via an S4 class. If this is something you'd find useful, i can certainly recommend this Package.]