Lists

A list is a sequence of values of the same type. There is no limit to the number of values that can be stored in a list. We notate lists as a sequence of comma-separated values inside square brackets.

> l1 :: [Double]
> l1 = [1.0,2.0,3.0,4.0]

> l2 :: [Int]
> l2 = undefined -- make a list of numbers

Lists can contain structured data...

> l3 :: [(Int,Bool)]
> l3 = [ (1,True), (2, False) ]

...and can be nested:

> l4 :: [[Int]]
> l4 = undefined

List elements must have the same type.

>-- l5 :: [Int]>-- l5 = [ 1 , True ] -- doesn't type check

(see the type error that results when you uncomment the definition above and reload the file in ghci.)

The empty list is written [] and pronounced "nil".

> l6 :: [a]
> l6 = []

Note: String is just another name for a list of characters ([Char]).

> l7 ::String> l7 = ['h','e','l','l','o',' ','5','5','2','!']

What happens when you print l7?

ghci> l7
undefined -- fill in with correct answer

"Cons"tructing Lists

The infix operator : constructs a new list, by adding a new element to the front of an existing list. (Note, the existing list is not modified.) We call this operator cons:

(Note that the a in the type of cons means that this function works for lists containing any type of element. In otherwords, we say that this function is polymorphic. More on this later.)

> cons :: a -> [a] -> [a]
> cons = (:)

> c1 :: [Char]
> c1 ='a': ['b', 'c']

> c2 :: [Int]
> c2 =1: []

Try printing c1 and c2

ghci> c1
undefined
ghci> c2
undefined

>-- undefined: fill in the type of c3> c3 = [] : []

Syntactic Sugar

GHC views the notation

[x1,x2, .. , xn]

as short for

x1 : x2 : .. : xn : []

This means that we can think of lists as a sequence of cons'ed elements, ending with nil. For example,

[1,2,3,4] and 1 : 2 : 3 : 4

are the same list:

ghci> [1,2,3,4] == 1:2:3:4:[]
True

Function practice: List Generation

Problem: Write a function that, given an argument x and a number n, returns a list containing n copies of x.

Step 1: Define test cases for the function.

We're using HUnit, a library for defining unit tests in Haskell. The (~?=) operator constructs a unit Test by comparing the actual result (first argument) with an expected result (second argument). Haskell is lazy, so these definitions create tests, but don't actually run them yet.

This function replicates any type of value, so the type of the first argument is polymorphic.

> clone :: a ->Int-> [a]

Step 3: Implement the function

We implement this function by recursion on the integer argument.

> clone x n =if n<=0then [] else x : clone x (n-1)

Step 4: Run the tests

The HUnit function runTestTT actually runs a given unit test and prints its result to the standard output stream. (That is why its result type is IO Counts. The IO in the type means that this computation does IO.)