From the book 'Programming in Scala' by Martin Odersky:
Another useful container object is the tuple. Like lists, tuples are immutable, but unlike lists, tuples can contain different types of elements.
But I can have:
val oneTwoThreee = List(1, 2, "Third Element") //same as:List.apply(1,2,3)
for (i <- 0 to 2) {
println(oneTwoThreee.apply((i)))
}
And its output is:
1
2
Third Element
So List in Scala can have different types of elements.
And from same book:
You may be wondering why you can’t access the elements of a tuple like the elements of a list, for example, with “pair(0)”. The reason is that a list’s apply method always returns the same type, but each element of a tuple may be a different type:
But as above code shows, List.apply() can return different types.
Am I missing something here regarding List and Tuples in Scala?
No, it can't. The type of your list is
List[Any]
, so all elements are of the same type:Any
.If you type your code into the Scala REPL, it will tell you at each step what the types are:
You can also always ask the Scala REPL for the type:
Any
is a very general, and thus very useless type, since it doesn't have any "interesting" methods. In fact, you are doing pretty much the only thing you can do with anAny
: representing it as aString
. That's why you're not noticing the problem, you accidentally happened to pick the only thing that works.Try multiplying the first and second element of your list:
No, it can't. Again let's just ask the Scala REPL what the types are:
As you can see, the type is always the same:
Any
.I think the main point Odersky is trying to show is that each tuple element can contain its own individual type, which allows using multiple different types. Something that a
List
can't do because a list is homogeneous, meaning if you want aList[Int]
, all elements of that list must beInt
values.If you look at the type of the list you created, you'll see that the compiler infers
List[Any]
, which is the common supertype of all Scala types. This means that if you want to do something concrete with the one of the elements in the list, i.e. it's head element which is of typeInt
, you can't because all the compiler knows about that element is that its of typeAny
, and you'll need to some how extract the underlying "concrete" type:While using a
Tuple3[Int, Int, String]
, actually "keeps" the concrete types:Now if we want to extract one of the
Int
values and increment them by 1, we can:If we tried doing the same with a
List[Any]
, the compiler would rightfully complain:The error is somewhat misleading, but this happens due to the fact
head
is actually of typeAny
.There is a more advanced possibility of using heterogeneous lists using shapeless and it's
HList
data type:Which yields:
In very short:
With a list, you cannot require that the first element must be a string, and the second, a number. With a tuple, you can, and the compiler will statically check that.
What follows is that you can have a list of an arbitrary length, because all elements are alike, but a tuple can be only of a fixed length, with the type of each element declared separately.
If you come from e.g. C background, tuples are like structs, and lists are like arrays.
A very simple demonstration of what the other answers are telling you.