In this section, we're going to introduce polymorphism, which is a way to define classes so that they can be used more flexibly. Before we get there, let's introduce a data structure that's truly fundamental for most functional languages, the immutable linked list. An immutable linked list is Constructed from two building blocks. Nil is the empty list, and Cons is a cell containing an element and the remainder of the list. Let's look at some examples for Cons list. The first list is list of 1, 2, 3, that would be represented as three Cons cells. Each con cell contains one of the elements of the list, and the remainder is the list that contains the other elements. The list 1, 2, 3 would start with a con cell containing the element 1 and would contain as the second half, the list 2, 3, and that would contain as the third half, the list three-Nil, and that finally has the rest Nil. You Construct lists recursively by adding progressively more and more elements at the front. List can also contain lists, and they also contain lists of different types. Here we have a more complicated list and list whose first element is a list that contains true and false, and to second element is a list of three. That would look as follows. Here you see the structure of the second list. If structure is a Con cell that contains a list as its first element. The rest of that list would be a list containing three as its element and Nil as its rest. To represent lists like this, we can define a class hierarchy. For the moment, let's keep it to list of integers only, so we would have a trait IntList at the top could also be an abstract class and two subclasses Cons, and Nil, that both extend IntList. The middle-class doesn't have any arguments. The Cons class takes two arguments, which is the head of the list and the tail of the list. That means the list is either the empty list, or it is a Cons object that contains a head x and a tail list xs. We used a new abbreviation here, where we defined a val head and a val tail directly as parameters of the list. That just means that the parameters are taken also as fields in the same list. A val head int val tail IntList means exactly the same thing as if we had defined parameters with some different names. Let's say _head and _tail, and then had defined to value definitions head, which is _head and tail, which is _tail. Using vals in parameter position, we can define at the same time parameters and fields of a class, which is sometimes handy. However, it seems too narrow to define only list with Int elements. We then need another class hierarchy for double lists and one for booleans and so on, one for each possible element types. That's clearly not sustainable. We can generalize the definition using a type parameter. The way we do that is that we add a type parameter [T] in brackets to the definition of class list and the definitions of the two subclasses, Cons and Nil. Type parameters are written in square brackets. That's how you see that you pass a type and not a value. Let's analyze this in more detail. We have a list of T, where [T] is the element type that has two subclasses. Cons of T is a Cons cell of type T, that would have a head of that very same type and a tail of type list of T. Whereas a Nil of T is another subclass of list of T. Using type parameters, we can give a complete definition of the list class hierarchy as follows. We have a base trait list of T. It has methods, is empty, head of type T, tail of type list of T. We have a Cons subclass that defines the head and the tail as the parameters that are given. That would contain already implementations for the head method here and the tail method here because the value parameter here also defines a field. All that needs to do is really to define the isEmpty method, which for Cons, a class would be false. Finally, we have the Nil subclass of list. For Nil is empty, is true, and for head and tail, we don't really know what to do because Nil doesn't have a head nor a tail. What we do instead, we throw an exception. If somebody would try to call head or tail on a Nil instance, they would get a no such element exception Nil.head and Nil.tail. Just like classes, functions can also have type parameters. For instance, we can define a function singleton that creates a list consisting of a single element where we leave open what the type of that element is. That means singleton takes a type parameter T and an element of type T, and it returns a cons instance with that type T and the element and Nil as a tail. That means we can now write singleton of intent one, that would give the list1 cons nil or a singleton Boolean and true, and that would give the list cons true Nil of type list of Boolean. In fact, the Scala compiler can usually find out what the correct type parameters are by looking at the value arguments of a function call. In most cases, type parameters can be left out, and that means concretely for singleton, you could also write singleton of one and singleton of true, and the compiler would figure out that the correct type parameter is int in the first case and Boolean in the second. So now that we have type parameters, do we have to change our model of evaluation by substitution? Turns out the answer is no. Type parameters do not affect evaluation in Scala in any way. In fact, one can assume that all type parameters and type arguments are removed before evaluating the program. So type parameters are a compiler-only property. This property is also called Type Erasure. Types get erased during the compilation process. Many languages that have type parameters use type Erasure, including Java, Scala, and most other functional languages like Haskell, ML, or OCaml. Some other languages keep the type parameters around it compile-time. These include C++ and.net languages like C# and F#. When we talk about type parameters, often the word polymorphism comes up. Polymorphism means that a function type comes in many forms. Poly means many and morphism means forms. In programming that means that a function can be applied to arguments of many different types, or also that a type can have instances of many types. We've seen those two forms of polymorphisms by now. In fact, the subtyping that arises in class hierarchies means that instances of a sub-class can be passed to a base class. That's sometimes called subtype polymorphism, and then we have the second form with type parameters, that's sometimes called generics. That means that instances of a function or class are created by type parameterization, so we can have many different instances that work on different types by parsing type parameters. Here's an exercise that uses type parameters. Write a function nth that takes a list and an integer and selects the nth element of the list. The signature of this function should look as follows. We have def nth. It should take a type parameter T, xs, which is a list of T and an index n, and it returns T, and you should fill in the body of the function indicated by the three question marks here. Elements in the list are conceptually numbered from zero, so nth of a list and zeros should return its first elements. If the index is outside the range from zero up to the length of the list minus 1, then an index out of bounds exception should be thrown with a throw statement. So I've copied the list class hierarchy into the worksheet. I've renamed all the class names to all uppercase to avoid conflicts with scholars predefined list and middle classes, and I've already given the outline of the nth method with the types as given, and the first-class of the nth message says, well, if the list here is empty, then we throw an IndexOutOfBoundsException because we can't take the nth element of an empty list. Then we have two other classes. One is if the index is zero, then we need to do something and we need to do something else if the index is non-zero. What do we need to do if the index is zero? In that case, we return the head of the list, the first element of the list, and if the index is not zero, then we do a recursive call to nth, with the list would be access to tail, and the index would be n-minus-1. We can test this by defining a list of three elements, one, two, three. That would be cons of one, cons of two, cons of three Nil, and take the second, the element at index two. So that would really be the third element because elements start at zero. Indeed that would be three. Can test it and say, well, what would we get for the 0th element? Yes, That's one. What would we get if we put in three here? Then we get a red underscore as we go, and it says that there was an index out of bounds exception as expected as we have implemented In the nth method that you saw.