In this section, we are going to introduce one of the most fundamental data structures in functional programming, which are lists. A list with elements x1 to xn is written list of x1 to xn, like you see here. For instance, here's a list of fruit, list of apples, oranges, pears, here's a list of numbers, here's the diagonal of a three-by-three matrix. That's a list that consists of three lists as sub-elements and here's the empty list. Lists resemble a lot arrays, which is another fundamental data structure used in imperative programs, but there are two important differences. The first difference is that lists are immutable. The elements of a list cannot be changed after the list is constructed, and the second difference is that lists are recursive while arrays are flat. To see that, let's draw the structure of some of the lists that you've seen here. Let's start with the first list of fruits. You see the fruit list consists of a cell which we call a cons-cell, and which we often write with a double colon. That con-cell has a head element which is called apples, and a tail, which is another list, which has a head element oranges and a tail element which is another list, and that third list has a head element called peas, and it's tail is nil. Lists are recursive. Every list contains, in its tail position another list, that can be nil. They're a little bit like Russian dolls, each doll contains a slightly smaller doll inside. The elements of this list are simple values, but it's also possible to design lists of lists. An example is the second list that we see here, diagonal 3. Here you see the structure of diagonal 3. It has at the top level, three cons nodes, 1, 2, 3, and each cons node has a head element that is another list, one of each of these three lists of three numbers each. Like arrays, lists are homogeneous. The elements of a list must all have the same type. The type of a lists with elements of type T is written in scala.List[T] or shorter just List [T]. If we add the types to the lists with defined before, then fruit would have type list of string because its elements are strings, nums has type list of int, diagonal 3 is a list of list of ints, its elements are list of ints, and finally, empty is a list of nothing. We will see the significance of this nothing value later on. We've seen that all lists are constructed from the empty list Nil and the construction operation, double colon, which is pronounced cons. X cons, xs gives a new list with the first a head element x, followed by the elements of xs, where we say those elements form the tail of the list. For instance, the fruitless that we've seen can be written like this, apple's cons oranges cons pears cons Nil, or the numbers lists can be written like this, or empty is simply Nil. In fact, we have a convention that, operators that end in a colon associate to the right. That means you can write A column B, column C, and it means the same thing as A double colon B, double colon C in parentheses, and that means we can omit the parentheses in the definitions above. While nums which was list of 1, 2, 3, 4, can be written 1 cons, 2 cons, 3 cons, 4 cons, Nil. There are three fundamental operations on lists and all other operations can be expressed in terms of those three. The first fundamental operation is head, which returns the first element of the list, the second fundamental operation is tail, which returns the list composed of all the elements except the first and the last fundamental operation is empty, which is true if the list is empty and false otherwise. These operations are all defined as methods on objects of type list. For instance, fruit.head would be apples, fruit.tail.head would be the second element of fruit that is oranges, diagonal3.head would be the list 1,0,0, and empty.head is not defined, in fact it throws an exception and no such element exception which says head of empty list. Instead of using head and tail, it's also possible to decompose lists with pattern matching, and that one is often preferred. Patterns for lists follow exactly the way we construct lists, so the Nil constant can be used as a pattern. The pattern p, cons ps is a pattern that matches a list with a head that matches p and a tail that matches ps. The pattern list of p1 to pn is actually the same as p1 cons, and so on pn cons Nil. It matches a list consisting exactly of n elements that each match patterns p1 to pn. Here are some examples of list patterns. One cons 2 cons xs. That matches all lists. Let's start with 1, followed by 2, and then can be followed by arbitrary elements that are captured in the variable xs. X column nil matches all lists of length 1. List of xs is the same as x colon nil also matches or lists of length 1. List simply like that is the empty list that's the same as nil. List of 2 column xs. What is that? Well, that matches a list that can contains 1 element, and that element is another list that starts with 2 and is followed by arbitrary elements. To check whether you've understood that, let's consider the pattern x cons y cons list xs, ys, con zs. What is the condition that describes most accurately the length L of the listed matches? You have six choices. The answer is it describes lists of length at least three. Let's see why, we have a pattern that matches headaches. Second element y, a third element that must be of this form, that's a single element, but it must be a list. That makes 1,2,3 elements. Then it can be followed by any tail zs, including the tail could be nil. If the tail is nil, the length of that list would be three. Some people might get confused and says, well I see 1,2,3,4 elements here, why does it not say list length greater or equal to four? It helps if we draw that list. That's the structure of the list that you see here above. You see indeed if you count the cons note here it's 1,2,3 and then it's followed by some arbitrary tail zs. The xs, ys, they don't count towards the length of the outer list because there make up together an element of that list, namely the third element of the outer list. Let's start with writing simple functions over lists. One common function is sorting a list of numbers in ascending order. There are several ways to do it. Here's a particularly simple one which is not very efficient. One way to sort the list 7,3,9,2 is to say, well, let's sort the tail of the list first. That would give the list 2,3,9. Then let's insert the element seven at the right position in that list. Functionally of course, we can't change the list, but we would return then a new list, which would be 2, 3, 7, 9. That idea describes this insertion sort, a particular sort algorithm. Here's the outline of insertion sort. It takes, if you work with integers, a list of integers and gives us back a list of integers. We do a pattern match if the list is empty, then we return the empty list. Otherwise, if the list is non-empty, then we recursively sort the tail of the list and we insert the head of the list into that sorted tail. It remains to define the insertion function that inserts an element x into a list xs at the right place, returning a list of the new elements. Again, we do a pattern match over what list it is. If we want to insert an element into the empty list, what would we get? Well, that's simply the list x. What about if it's a non-empty list? What do we need to do in the case where the list is not empty, lets say it has a head y and a tail ys. Then it depends whether the element we want to insert the x here, is less than y or greater or equal to y. If the element we want to insert is less than y, then it will be the first element of the result list because we start with smallest elements first. We simply return x and then xs the list itself. If the element is greater or equal to the head element y, then the list keeps its old head element and we insert x recursively into the tail of the list. That leads us to do this x branch. Now if we look at complexity, what's the worst-case complexity of insertion sort relative to the length of the input list N. Here we see that insertion sort would go linearly through each element. It has one pattern matching, one operation per element of the list. Each of those operations cause the insert call on the remaining list. What's the complexity of insert? Well, insert is again, it's worst-case complexity is it has to go through the whole list. That would give a complete complexity proportional to N times N, where N is the length of the list. The first N comes from essentially having to insert each element of the outer list, and the second N comes from having to traverse the inner list, potentially up to N, for the longest and then successively smaller ones, but it's still N times N. Here, that's actually wrong because the question was, what's the worst-case complexity of insertion sort? The worst-case complexity of insert is proportional to N. That means the worst-case complexity of insertion sort is proportional to N times N.