Functional Programming Lecture 10 type checking Strong Typing

  • Slides: 17
Download presentation
Functional Programming Lecture 10 type checking

Functional Programming Lecture 10 type checking

Strong Typing Haskell is a strongly typed programming language. This means that every value

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 length. Int : : [Int] -> Int (polymorphic) (monomorphic)

Monomorphic Type Checking Expression: a literal, a variable, a constant, a list, a tuple,

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] In words, If x has type t _____ [] : : [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 xn

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

Inference rule for type checking a function application: f : : s -> t

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 fe result has type t Examples: Show 2 + Int 3 : : Int Show (ord ‘c’) + Int True is not well-typed.

Type Checking Function Definitions To check a function f : : t 1 ->

Type Checking Function Definitions To check a function f : : t 1 -> t 2 -> … -> tk -> t when there are several cases, i. e. f p 1 p 2 … pk | g 1 = e 1 | g 2 = e 2 … | gl = el We need to check that - each of the guards gi is of type Bool - the value of each ei is of type t - each pattern pj matches some element of type tj. 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

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: : t 1 -> t 2 -> t 3 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

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]

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 don’t know anything else about zs. Therefore, g : : (Int, [b]) -> Int.

Function Composition Two functions can be composed in Haskell, just as in Maths. Consider

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

Function Composition h= f 2 (b -> c) the input . f 1 (a

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

Now consider the type of h = g. f g : : (Int, [d])

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]) c=Int a=(e, Char) 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 is the process of matching two terms, that is making substitutions to each

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]) (Char, [Char]) (a->a, [Char]) … (e, [Char]) (Int, [Int]) (Int, [Bool]) (Int, [[c]]) (Int, [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

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] a -> [a] no solution a=Int, a=[Int]

Type checking polymorphic function application Assume f : : s -> t e :

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

Type checking polymorphic function application Example: map : : (a -> b) -> [a]

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 :

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 won’t know which type a to check as a Show type! > f [] • ERROR: Unresolved overloading • *** type : Show a => [Char] • *** expression : print 1 [] Solution: > f ([]: : String) “{}” NB. You could force [] to have any list of Show type.