# Functional Programming Lecture 10 - type checking.

## Presentation on theme: "Functional Programming Lecture 10 - type checking."— Presentation transcript:

Functional Programming Lecture 10 - type checking

Strong Typing Haskell is a strongly typed programming language. This means that every value has a defined type, and we can check that type before evaluating the value. Benefits - many errors are caught before run-time - use types to categorise and lookup functions A type may be monomorphic, polymorphic, or an instance of a type class. Note: we can always make a monomorphic instance of a polymorphic type. E.g. length :: [a] -> Int (polymorphic) length Int :: [Int] -> Int (monomorphic)

Monomorphic Type Checking Expression:a literal, a variable, a constant, a list, a tuple, or a function application. Inference rules for type checking a list: x:: t xs :: [t] _________ x:xs :: [t] [] :: [t] In words, If x has type t and xs is a list of t x:xs then result has type list of t Examples: Show [True, False] :: [Bool] Show [3,True] is not well-typed.

Inference rule for type checking a tuple: x 1 :: t 1 x n :: t n (x 1, …., x n ) :: (t 1, …., t n ) In words, if each component x i has type t i (x 1, …., x n ) then result has type tuple (t 1, …., t n ) Examples: Show (True,3) :: (Bool,Int)

Inference rule for type checking a function application: f :: s -> t e :: s f e :: t In words, f has type s -> t e has type s f e result has type t Examples: Show 2 + Int 3 :: Int Show (ord c) + Int 3 :: Int Show (ord c) + Int True is not well-typed.

Type Checking Function Definitions To check a function f :: t 1 -> t 2 -> … -> t k -> t when there are several cases, i.e. f p 1 p 2 … pk | g 1 = e 1 | g 2 = e 2 … | g l = e l We need to check that - each of the guards g i is of type Bool - the value of each e i is of type t - each pattern p j matches some element of type t j. E.g. pattern p:q matches an element of type [t] when p matches an element of t and q matches an element of [t].

Type Checking Function Definitions Example f :: Int -> [Int] -> Int f a (x:xs) | (a>x) = a + x | (a ==x) = x Note: | (x >xs) = a type error Can we deduce type of f? Let (x:xs):: [b], then x::b. a has same type as x, so a::b. b must be numeric, assume b=Int. f:: t1 -> t2 -> t3 p 1 =a, so p 1 matches an element of Int p 2 =x:xs so p 2 matches an element of [b] = [Int] e 1 =a + x, a has type Int So f :: Int -> [Int] -> Int

Polymorphic Type Checking Type checking a polymorphic type is the same as solving type constraints. Some examples: Consider f (x,y) = (x,[a.. y]) a pair a character - the argument to f is a pair - x is completely unconstrained - y is an expression in an enumerated type, starting with a Char, so y must have type Char - [a.. y] has type [Char] - the rhs is a pair. Therefore, f :: (a,Char) -> (a, [Char])

Consider g (m,zs) = m + length zs a pair adds values [b] -> Int - m must have a numeric type - zs must be a list type, call it [b] - length zs :: Int, so this forces m to have type Int - since m has type Int, the rhs must have type Int - we dont know anything else about zs. Therefore, g :: (Int,[b]) -> Int.

Function Composition Two functions can be composed in Haskell, just as in Maths. Consider composition of f1 and f2. f2.f1 a a f1 b f2 c c f1 a = b, f2 b = c. f2.f1 takes in a and outputs c. Definition (f2. f 1) x = f2 (f1 x) -- the output of f1 becomes the input of f2 The type of _._ is _._ :: (b -> c) -> (a -> b) -> (a -> c) type of f2 type of f1 type of f2.f1 -

Function Composition h = f2. f1 (b -> c) (a -> b) the input the output result is function from input to output h :: a -> c

Now consider the type of h = g. f g :: (Int,[d]) -> Int -- rename type var b as d g (m,zs) = m + length zs f :: (e,Char) -> (e, [Char]) -- rename type var a as e f (x,y) = (x,[a.. y]) The type of _._ is _._ :: (b -> c) -> (a -> b) -> (a -> c) g f Therefore the type of g. f is b=(Int, [d]) a=(e,Char) c=Int b=(e,[Char]) We have to unify the two definitions of b: (Int,[d]) = (e,[Char]) Solution: e=Int, d=Char Therefore, h :: a -> c h :: (Int,Char) -> Int -

Unification Unification is the process of matching two terms, that is making substitutions to each term. How do you describe the unification of (e,[Char]) and (Int,[d])? (Bool, [Char]) (Int,[Int]) (Char,[Char]) (Int, [Char]) (Int,[Bool]) (a->a, [Char]) (Int, [[c]]) … … (e, [Char]) (Int,[d]) The unification of two expressions is the intersection of the sets of types of those two expressions. This intersection is given by most general common instance of the two expressions. The unifier is the subsitution: e = Int, d = Char.

Unification Not all expressions can be unified. For example, consider [Int] -> [Int] and a -> [a]. These constraints are inconsistent, the two expressions cannot be unified. [Int] -> [Int] no solution a -> [a] a=Int,a=[Int]

Type checking polymorphic function application Assume f :: s -> t e :: u f e has type has type s -> t u unify s and u by s s -> t s

Type checking polymorphic function application Example: map :: (a -> b) -> [a] -> [b] ord :: Char -> Int What is type of map ord? Must unify a->b and Char -> Int. So, a=Char, b=Int. map ord :: [a] -> [b] map ord (a ->b) -> [a]->[b] Char -> Int s -> t u (Char -> Int) ->[Char] ->[Int] s -> t Therefore map ord :: [a] -> [b] map ord :: [Char]-> [Int] Note: the result type may still contain type variables.

Resolving Type Ambiguity A final note on types. Assume you have defined: f :: Show a => [a] -> String -- f converts lists of a to string format, e.g. sets f [] = {} f (x:xs) = { ++ ( g (x:xs)) ++ } where g [x] = show a g x:y:xs = (show a) ++, ++ …. Then f [] will be ambiguous, that is, it wont know which type a to check as a Show type! > f [] ERROR: Unresolved overloading *** type : Show a => [Char] *** expression : print1 [] Solution: > f ([]::String) {} NB. You could force [] to have any list of Show type.