Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 11 - Part IIK. Louden, Programming Languages1 Exercises [’a’,’b’,’c’] (’a’,’b’,’c’) [(False,’0’),(True,’1’)] ([False,True],[’0’,’1’]) [tail,init,reverse]

Similar presentations


Presentation on theme: "Chapter 11 - Part IIK. Louden, Programming Languages1 Exercises [’a’,’b’,’c’] (’a’,’b’,’c’) [(False,’0’),(True,’1’)] ([False,True],[’0’,’1’]) [tail,init,reverse]"— Presentation transcript:

1 Chapter 11 - Part IIK. Louden, Programming Languages1 Exercises [’a’,’b’,’c’] (’a’,’b’,’c’) [(False,’0’),(True,’1’)] ([False,True],[’0’,’1’]) [tail,init,reverse] What are the types of the following values?(1)

2 Chapter 11 - Part IIK. Louden, Programming Languages2 second xs = head (tail xs) swap (x,y) = (y,x) pair x y = (x,y) double x = x*2 palindrome xs = reverse xs == xs twice f x = f (f x) What are the types of the following functions?(2) Check your answers using Hugs.(3)

3 Chapter 11 - Part IIK. Louden, Programming Languages3 Curried Functions mult:: Int -> Int -> Int mult x y = x * y We could read this as a function taking two Int arguments and returning a third. OR-- a function taking a single Int argument and returning a function from Int -> Int as a result. Moral: the mult function can take either one or two parameters; if you give it one, it returns a function of the (missing) 2nd parameter Ex: map (mult 3) [1,2,3] Yields [3,6,9]

4 Chapter 11 - Part IIK. Louden, Programming Languages4 Curried Functions Contrast the previous definition of mult with a definition using a tuple: mult2 :: (Int,Int) -> Int mult2 (x,y) = x *y Now we must always supply both parameters at every call: The mult function is Curried (named after Haskell B. Curry), the mult 2 function is not.

5 Chapter 11 - Part IIK. Louden, Programming Languages5 Curried Functions Definition: A function taking multiple parameters is Curried if it can be viewed as a (higher-order) function of a single parameter. Currying is good, since all functions can be viewed as having just a single parameter, and higher-order functions can be obtained automatically.

6 Chapter 11 - Part IIK. Louden, Programming Languages6 Haskell A fully-Curried lazy purely functional language with Hindley-Milner static typing. (Fully-Curried means all functions, including built-in arithmetic, are implicitly Curried.) Has many other features that make it perhaps the most advanced functional language available today. Includes overloading and a built-in mechanism for generator-filter programming (called list comprehensions).

7 Chapter 11 - Part IIK. Louden, Programming Languages7 Sample Haskell code: fact n = if n == 0 then 1 else n * fact (n-1) square x = x * x pi1 = 3.14159 gcd1 u v = if v == 0 then u else gcd1 v (mod u v) squarelist lis = if (null lis) then lis else square (head lis): squarelist (tail lis)

8 Chapter 11 - Part IIK. Louden, Programming Languages8 Haskell properties Parentheses are only necessary for grouping. Semicolons are usually unnecessary. Definitions have no special syntax, so they must be loaded from a file—they cannot be entered directly into an interpreter. Operators are infix. Lists are written using square brackets and commas (e.g. [1,2,3] ), with ++ as the list append operation, and : (colon) as the list constructor operation. All value and function names must start with a lowercase letter, and all types are uppercase: Int. Names cannot be redefined unless you state you are overriding them - hence the use of pi1 & gcd1 in the previous slide ( pi & gcd are predefined).

9 Chapter 11 - Part IIK. Louden, Programming Languages9 Haskell properties (2) Haskell is purely functional, so there are no variables or assignments, and I/O and sequencing cannot be done except using special tools, called monads (which we do not study here). Of course, there are still local definitions (in other words no value is being stored, we are just defining the pattern): let x = 2; y = 3 in x + y or: let x = 2 y = 3 in x + y Note indentation in the previous code to get rid of the semicolon: Haskell uses a two-dimensional Layout Rule to remove extra syntax. Leading white space matters!

10 Chapter 11 - Part IIK. Louden, Programming Languages10 Haskell properties (3) All local definitions are recursive: f x = let z = x-1 g = \y->if y==0 then z else g(y-1) in g x + 2 All expressions are delayed in Haskell: ones = 1:ones -- can also write [1,1..] ints_from n = n : ints_from (n+1) ints = ints_from 1 -- also: [1..] Infix operators can be made prefix by putting parentheses around them: doubleIt lis = map ((*) 2) lis Note lambda syntax Comment

11 Chapter 11 - Part IIK. Louden, Programming Languages11 Haskell properties (4) Hindley-Milner type checking is performed on all definitions and expressions. Type variables use the names a, b, c, etc. Types are not automatically displayed, but can be viewed by using the :t command in the interpreter. Small sample interpreter session: > [1]++[2] [1,2] > :t (++) (++) :: [a] -> [a] -> [a] > [1]++['a'] – wants types to match error: No instance for (Num Char) arising from the literal `1' at :1:1 Probable fix: add an instance declaration for (Num Char) In the list element: 1 In the first argument of `(++)', namely `[1]' In the definition of `it': it = [1] ++ ['a']

12 Chapter 11 - Part IIK. Louden, Programming Languages12 Strong typing Checks whether or not expressions we wish to evaluate or definitions we wish to use obey typing rules without any evaluation taking place. A pattern is consistent with a type if it will match some elements of that type. –A variable is consistent with any type –A pattern (t:ts) is consistent with [p] if t is consistent with p and ts is consistent with [p]

13 Chapter 11 - Part IIK. Louden, Programming Languages13 Type Checking f:: a->b->c f x y | g1 = e1 | g2 = e2 |otherwise = e3 We must check 1. g1 and g2 are boolean 2. x is consistent with a and y is consistent with b 3. e1,e2,e3 are all of type c.

14 Chapter 11 - Part IIK. Louden, Programming Languages14 Examples of type inference f (x,y) = (x,[‘a’..y]) What is the type of f? The argument of f is a pair. We consider separately the constraints of x and y. Y is used with [‘a’..y] so it must be a Char. f :: (a, Char) -> (a, [Char])

15 Chapter 11 - Part IIK. Louden, Programming Languages15 Examples of type inference g(m,z) = m + length z What constraints are placed on m and z? M must be numeric, as used with +. z must be a list as used with length. Furthermore, since length returns an int, we assume m is also an Int. Now consider the function composition The input of g must be the output of f g. f :: (Int, Char) -> Int *Main>

16 Chapter 11 - Part IIK. Louden, Programming Languages16 Unification We describe the intersection of the sets given by two type expressions. The unification of the two is the most general common instance of the two type expressions.

17 Chapter 11 - Part IIK. Louden, Programming Languages17 Unification need not result in a monotype. (a,[a]) and ([b],c) unify as ([b], [[b]])

18 Chapter 11 - Part IIK. Louden, Programming Languages18 Patterns in Haskell Patterns can be used in function definitions Typical patterns are 0 for zero, [] for the empty list, and (x:xs) for a nonempty list. fact and squarelist : fact 0 = 1 fact n = n * fact (n-1) squarelist [] = [] squarelist (x:xs) = (square x) : squarelist xs Of course, it would be better to use map : squarelist lis = map square lis

19 Chapter 11 - Part IIK. Louden, Programming Languages19 Patterns in Haskell (cont.) Because of patterns, it is unusual in Haskell to use head, tail, and null. The anonymous wildcard pattern (matches anything) is the underscore in Haskell. Overlapping patterns are ok (see fact on previous slide). Non-exhaustive patterns can also be used, and do not generate a warning Patterns are syntactic sugar for case expressions: fact n = case n of 0 -> 1 _ -> n * fact (n-1)

20 Chapter 11 - Part IIK. Louden, Programming Languages20 Haskell List Comprehensions Generators and filters can be expressed together in Haskell in a quasi-list notation called a list comprehension: odds = [n | n <- [1..], mod n 2 /= 0] -- can also write [1,3..] Multiple generators and filters can be combined in a single list comprehension: mystery = [n+m|n <-[1..], m <-[1..], mod n m /= 0] List comprehensions allow many snappy programs to be written: (mod without quotes is prefix) sieve (h:t) = h:sieve [n|n <- t, mod n h /= 0] primes = sieve [2..]

21 Chapter 11 - Part IIK. Louden, Programming Languages21 Haskell Data Structures So far we have only seen lists in Haskell, although we know that static typing does not allow lists to imitate structs or classes as in Scheme. Haskell has built-in tuple types. which are Cartesian products (like struct s but with no field names): intWithRoot:: Int -> (Int,Double) intWithRoot x = (x, sqrt (fromInt x)) Use patterns to get the components out: rootOf x = let (_,r)=intWithRoot x in r Tuple types are written the same way values are,

22 Chapter 11 - Part IIK. Louden, Programming Languages22 Haskell Data Structures (2) Lists and tuples do not go far enough, since unions cannot be expressed. User-defined types can be introduced using data definitions: data Direction = North|East|South|West Haskell can have polymorphic types and constructors with more than one parameter data Either1 a b = Left1 a | Right1 b fun ::Either1 a b -> Bool fun (Left1 _) = True fun (Right1 _) = False fun (Left1 5) True :t Left1 "hi" Left1 "hi" :: Either1 [Char] b

23 Chapter 11 - Part IIK. Louden, Programming Languages23 Haskell Data Structures (3) Note how the previous definition expresses a tagged union of two polymorphic types. Binary search tree example (recursive type): data BST a = Nil | Node a (BST a) (BST a) simpleBST = Node "horse" Nil Nil -- value Constructors can be used as patterns: tree_to_list Nil = [] tree_to_list (Node val left right) = (tree_to_list left) ++ [val] ++ (tree_to_list right) Type synonyms can also be defined: type IntDouble = (Int,Double) Note: all type and constructor names must be uppercase.

24 Chapter 11 - Part IIK. Louden, Programming Languages24 member a -> [a] ->Bool Can check for equality, but this only works when a is a type for which equality is defined. How do we express that? We call the collection of types over which a function is defined to be the type class. For instance, the set of types over which == is defined is the equality class.

25 Chapter 11 - Part IIK. Louden, Programming Languages25 How do we define such a class We say what is needed for a type a to be in a class. In this case, we need == defined over a. In other words, a ->a->Bool class Eq a where (==) :: a-> a-> Bool Members of a type class are called its instances. Functions from Int->Int are NOT of type Eq since there is no algorithm to decide if two functions have the same behavior

26 Chapter 11 - Part IIK. Louden, Programming Languages26 Overloading in Haskell Many functions in Haskell are overloaded, in that they can take values from a (finite) number of different types. An easy example is the square function, defined by square x = x * x. The type of square in Haskell is: square :: Num a => a -> a This says basically that square is defined for any Num a type (such types all have a * function). The type Num a => a is called a qualified type, and Num is called a type class. Type classes are a bit like Java interfaces: they require that a certain function be defined, and each associated type must then implement it.

27 Chapter 11 - Part IIK. Louden, Programming Languages27 Overloading in Haskell (2) Here is an example of how to define a Sizeable type class that provides a measure of the size of a piece of data: class Sizeable a where size:: a -> Int Now any type that you want to implement Sizeable must be declared an instance of the Sizeable class: (i.e., belongs to that class): instance Sizeable [a] where size = length instance Sizeable (BST a) where size Nil = 0 size (Node d l r) = (size l)+(size r) + 1

28 Chapter 11 - Part IIK. Louden, Programming Languages28 Overloading in Haskell (3) Now any use of the size function automatically adds the Sizeable qualification to a type: trivial x = size x == 0 This function has type: Sizeable a => a -> Bool Type classes usually require multiple functions: class Num a where (+), (-), (*) :: a -> a -> a negate :: a -> a abs :: a -> a...etc. instance Num Int where (+) = primPlusInt (-) = primMinusInt negate = primNegInt...etc. Built-in "hidden" definitions

29 Chapter 11 - Part IIK. Louden, Programming Languages29 Overloading in Haskell (4) Type classes may need to "inherit" functions from other type classes (similar to interface inheritance in Java): class Eq a where (==), (/=) :: a -> a -> Bool x == y = not (x/=y) x /= y = not (x==y) class (Eq a) => Ord a where ( =),(>) :: a -> a -> Bool max, min :: a -> a -> a instance Eq Int where … instance Ord Int where... Note default definitions

30 Chapter 11 - Part IIK. Louden, Programming Languages30 Numeric Type Class Hierarchy is read “is an instance of”

31 Chapter 11 - Part IIK. Louden, Programming Languages31 Overloading in Haskell (5) The Show class allows a data type to be displayed as a String (e.g. by the interpreter): class Show a where show :: a -> String So many data types need to be made instances of Show and Eq that Haskell can do it automatically: data BST a = Nil | Node a (BST a) (BST a) deriving (Show,Eq) Overloading presents challenges for Hindley-Milner type checking that result in some surprises: squarelist = map square gives squarelist the type [Integer] -> [Integer] (no overloading!)


Download ppt "Chapter 11 - Part IIK. Louden, Programming Languages1 Exercises [’a’,’b’,’c’] (’a’,’b’,’c’) [(False,’0’),(True,’1’)] ([False,True],[’0’,’1’]) [tail,init,reverse]"

Similar presentations


Ads by Google