RECURSIVE LIST PROCESSING CONTINUED

Concatenating sublists: ➜ Lists can be nested, e.g., [[5, 6, 2], [], [4, 2]] :: [[Int]] ➜ concat merges sublists into one list, e.g., concat [[5, 6, 2], [], [4, 2]] ⇒ [5, 6, 2, 4, 2] ➜ We may specify the functionality of concat as

concat [xs1, xs2, ... , xsn] = xs1 ++ xs2 ++ ··· ++ xsn ✧

Full definition: concat :: [[a]] -> [a] concat [] = [] concat (xs:xss) = xs ++ concat xss

RECURSIVE LIST PROCESSING CONTINUED 1 RECURSIVE LIST PROCESSING CONTINUED

Concatenating sublists: ➜ Lists can be nested, e.g., [[5, 6, 2], [], [4, 2]] :: [[Int]] ➜ concat merges sublists into one list, e.g., concat [[5, 6, 2], [], [4, 2]] ⇒ [5, 6, 2, 4, 2] ➜ We may specify the functionality of concat as

concat [xs1, xs2, ... , xsn] = xs1 ++ xs2 ++ ··· ++ xsn ✧

Full definition: concat :: [[a]] -> [a] concat [] = [] concat (xs:xss) = xs ++ concat xss

RECURSIVE LIST PROCESSING CONTINUED 1-A Appending a list: ➜ The function ++ is an infix operator (Functions with alphanumeric names are used prefix) (++) :: [a] -> [a] -> [a] ➜ ++ consumes and simultaneously produces a list ➜ Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) ⇒ 5:(6:(2:(4:(2:[])))) ➜ What can we observe? Essentially, ++ replaces the [] of the first list with the second list. ✧

Full definition: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

RECURSIVE LIST PROCESSING CONTINUED 2 Appending a list: ➜ The function ++ is an infix operator (Functions with alphanumeric names are used prefix) (++) :: [a] -> [a] -> [a] ➜ ++ consumes and simultaneously produces a list ➜ Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) ⇒ 5:(6:(2:(4:(2:[])))) ➜ What can we observe? Essentially, ++ replaces the [] of the first list with the second list. ✧

Full definition: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

RECURSIVE LIST PROCESSING CONTINUED 2-A Appending a list: ➜ The function ++ is an infix operator (Functions with alphanumeric names are used prefix) (++) :: [a] -> [a] -> [a] ➜ ++ consumes and simultaneously produces a list ➜ Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) ⇒ 5:(6:(2:(4:(2:[])))) ➜ What can we observe? Essentially, ++ replaces the [] of the first list with the second list. ✧

Full definition: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

RECURSIVE LIST PROCESSING CONTINUED 2-B Appending a list: ➜ The function ++ is an infix operator (Functions with alphanumeric names are used prefix) (++) :: [a] -> [a] -> [a] ➜ ++ consumes and simultaneously produces a list ➜ Let us look at [5, 6, 2] ++ [4, 2]: (5:(6:(2:[]))) ++ (4:(2:[])) ⇒ 5:(6:(2:(4:(2:[])))) ➜ What can we observe? Essentially, ++ replaces the [] of the first list with the second list. ✧

Full definition: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

RECURSIVE LIST PROCESSING CONTINUED 2-C Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

Let us look at [5, 6, 2] ++ [4, 2]:

(5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5)

RECURSIVE LIST PROCESSING CONTINUED 3 Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

Let us look at [5, 6, 2] ++ [4, 2]:

(5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5)

RECURSIVE LIST PROCESSING CONTINUED 3-A Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

Let us look at [5, 6, 2] ++ [4, 2]:

(5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5)

RECURSIVE LIST PROCESSING CONTINUED 3-B Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

Let us look at [5, 6, 2] ++ [4, 2]:

(5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5)

RECURSIVE LIST PROCESSING CONTINUED 3-C Stepwise execution of ++: (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

Let us look at [5, 6, 2] ++ [4, 2]:

(5:(6:(2:[]))) ++ (4:(2:[])) (1) ⇒ 5:( (6:(2:[])) ++ (4:(2:[])) ) (2) ⇒ 5:(6:( (2:[]) ++ (4:(2:[])) )) (3) ⇒ 5:(6:(2:( [] ++ (4:(2:[])) ))) (4) ⇒ 5:(6:(2:( 4:(2:[]) ))) (5)

RECURSIVE LIST PROCESSING CONTINUED 3-D POLYMORPHISM ➜ We used head, tail, null, concat, and ++ only on lists containing integers ➜ But they are not restricted to integer lists

head ["a string", "another string"] ⇒ "a string" null [2.3, 4.1, 9.52] ⇒ False [False, True, True] ++ [False] ⇒ [False, True, True, False]

Thus, we call them polymorphic functions.

POLYMORPHISM 4 Types of polymorphic functions: head :: [a] -> a tail :: [a] -> [a] null :: [a] -> Bool concat :: [[a]] -> [a] (++) :: [a] -> [a] -> [a] fst :: (a, b) -> a snd :: (a, b) -> b

➜ We call the a in [a] a type variable ➜ A type variable is a placeholder for a concrete type (just as an argument variable is placeholder for a concrete argument value) ✧

POLYMORPHISM 5 Types of polymorphic functions: head :: [a] -> a tail :: [a] -> [a] null :: [a] -> Bool concat :: [[a]] -> [a] (++) :: [a] -> [a] -> [a] fst :: (a, b) -> a snd :: (a, b) -> b

➜ We call the a in [a] a type variable ➜ A type variable is a placeholder for a concrete type (just as an argument variable is placeholder for a concrete argument value) ✧

POLYMORPHISM 5-A (++) :: [a] -> [a] -> [a] -- general type

➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter

POLYMORPHISM 6 (++) :: [a] -> [a] -> [a] -- general type

➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter

POLYMORPHISM 6-A (++) :: [a] -> [a] -> [a] -- general type

➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter

POLYMORPHISM 6-B (++) :: [a] -> [a] -> [a] -- general type

➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter

POLYMORPHISM 6-C (++) :: [a] -> [a] -> [a] -- general type

➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter

POLYMORPHISM 6-D (++) :: [a] -> [a] -> [a] -- general type

➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter

POLYMORPHISM 6-E (++) :: [a] -> [a] -> [a] -- general type

➜ When using a polymorphic function, we can instantiate the type variables to any concrete type: (++) :: [Int] -> [Int] -> [Int] -- These are (++) :: [Float] -> [Float] -> [Float] -- instances of (++) :: [Bool] -> [Bool] -> [Bool] -- (++) ➜ Consistency: All occurrences of a type variable have to be replaced by the same concrete type (++) :: [Int] -> [Bool] -> [Int] -- WRONG! ➜ Important: Type variables must start with a lowercase letter Good names: a, ty, elem, elem1, elementType Incorrect names: A, Elem, 1elem ➜ Conversely, names of concrete types always start with an uppercase letter

POLYMORPHISM 6-F Some more terminology: ➜ The polymorphism that we just discussed is more precisely called ➜ For example, fst :: (a, b) -> a fst (x, y) = x has two type parameters a and b ➜ Independent of the type parameters, the implementation of fst is always the same ➜ There is a second form of polymorphism called ad-hoc polymorphism ➜ Ad-hoc polymorphism is also often called overloading ➜ With ad-hoc polymorphism, the code of the function varies with varying type parameters ➜ Examples: • (+) applied to Int or Float • (==) applied to Int or [Bool]

POLYMORPHISM 7 Some more terminology: ➜ The polymorphism that we just discussed is more precisely called parametric polymorphism ➜ For example, fst :: (a, b) -> a fst (x, y) = x has two type parameters a and b ➜ Independent of the type parameters, the implementation of fst is always the same ➜ There is a second form of polymorphism called ad-hoc polymorphism ➜ Ad-hoc polymorphism is also often called overloading ➜ With ad-hoc polymorphism, the code of the function varies with varying type parameters ➜ Examples: • (+) applied to Int or Float • (==) applied to Int or [Bool]

POLYMORPHISM 7-A OVERLOADING (AD-HOC POLYMORPHISM) ➜ Operations like +, *, and == are overloaded ➜ What about the type of product :: Num a => [a] -> a product [] = 1 product (x:xs) = x * product xs ✧ ➜ Members of the Num are, e.g., Int and Float ➜ Num a => ... means “for all a that are a member of type class Num, we have ...” ➜ Other important type classes are Eq and Ord: (==) :: Eq a => a -> a -> Bool (/=) :: Eq a => a -> a -> Bool (>) :: Ord a => a -> a -> Bool ...

OVERLOADING (AD-HOC POLYMORPHISM) 8 OVERLOADING (AD-HOC POLYMORPHISM) ➜ Operations like +, *, and == are overloaded ➜ What about the type of product :: Num a => [a] -> a product [] = 1 product (x:xs) = x * product xs ✧ ➜ Members of the type class Num are, e.g., Int and Float ➜ Num a => ... means “for all a that are a member of type class Num, we have ...” ➜ Other important type classes are Eq and Ord: (==) :: Eq a => a -> a -> Bool (/=) :: Eq a => a -> a -> Bool (>) :: Ord a => a -> a -> Bool ...

OVERLOADING (AD-HOC POLYMORPHISM) 8-A OVERLOADING (AD-HOC POLYMORPHISM) ➜ Operations like +, *, and == are overloaded ➜ What about the type of product :: Num a => [a] -> a product [] = 1 product (x:xs) = x * product xs ✧ ➜ Members of the type class Num are, e.g., Int and Float ➜ Num a => ... means “for all a that are a member of type class Num, we have ...” ➜ Other important type classes are Eq and Ord: (==) :: Eq a => a -> a -> Bool (/=) :: Eq a => a -> a -> Bool (>) :: Ord a => a -> a -> Bool ...

OVERLOADING (AD-HOC POLYMORPHISM) 8-B OVERLOADING (AD-HOC POLYMORPHISM) ➜ Operations like +, *, and == are overloaded ➜ What about the type of product :: Num a => [a] -> a product [] = 1 product (x:xs) = x * product xs ✧ ➜ Members of the type class Num are, e.g., Int and Float ➜ Num a => ... means “for all a that are a member of type class Num, we have ...” ➜ Other important type classes are Eq and Ord: (==) :: Eq a => a -> a -> Bool (/=) :: Eq a => a -> a -> Bool (>) :: Ord a => a -> a -> Bool ...

OVERLOADING (AD-HOC POLYMORPHISM) 8-C OVERLOADING (AD-HOC POLYMORPHISM) ➜ Operations like +, *, and == are overloaded ➜ What about the type of product :: Num a => [a] -> a product [] = 1 product (x:xs) = x * product xs ✧ ➜ Members of the type class Num are, e.g., Int and Float ➜ Num a => ... means “for all a that are a member of type class Num, we have ...” ➜ Other important type classes are Eq and Ord: (==) :: Eq a => a -> a -> Bool (/=) :: Eq a => a -> a -> Bool (>) :: Ord a => a -> a -> Bool ...

OVERLOADING (AD-HOC POLYMORPHISM) 8-D AN EXERCISE

Consider a function that reverses the elements in a list:: ➜ It’s type is reverse :: [a] -> [a] ➜ An example of it’s use is reverse [1, 2, 3] = [3, 2, 1] ➜ Finally, we might specify it as

reverse [xs1, xs2, ... , xsn] = [xsn, ··· , xs2, xsn] ➜ How can you define reverse as a recursive function?

AN EXERCISE 9 AN EXERCISE

Consider a function that reverses the elements in a list:: ➜ It’s type is reverse :: [a] -> [a] ➜ An example of it’s use is reverse [1, 2, 3] = [3, 2, 1] ➜ Finally, we might specify it as

reverse [xs1, xs2, ... , xsn] = [xsn, ··· , xs2, xsn] ➜ How can you define reverse as a recursive function?

AN EXERCISE 9-A