Presentation is loading. Please wait.

Presentation is loading. Please wait.

Functional Programming

Similar presentations


Presentation on theme: "Functional Programming"— Presentation transcript:

1 Functional Programming
Universitatea Politehnica Bucuresti Adina Magda Florea

2 Lecture No. 8 & 9 The Haskell Programming Language Introduction
Types and classes Defining functions List comprehensions String comprehensions Recursive functions

3 Haskell - history 1987 – an international committee of researchers initiates the development of Haskell, a standard lazy functional language The comittee publishes the Haskell 98 report, defining a stable version of the language

4 1. Haskell - introduction
Haskell is a typeful programming language: types are pervasive (unlike Scheme) Because Haskell is a purely functional language, all computations are done via the evaluation of expressions (syntactic terms) to yield values (abstract entities that we regard as answers). Every value has an associated type. 5 :: Integer 'a' :: Char inc :: Integer -> Integer [1,2,3] :: [Integer] ('b',4) :: (Char,Integer)

5 Haskell - introduction
All Haskell values are "first-class" - they may be passed as arguments to functions, returned as results, placed in data structures, etc. Haskell types, on the other hand, are not first-class. Types describe values, and the association of a value with its type is called a typing.

6 Haskell - introduction
Functions in Haskell are normally defined by a series of equations. For example, the function inc can be defined by the single equation: inc n = n+1 An equation is an example of a declaration. Another kind of declaration is a type signature declaration with which we can declare an explicit typing for inc: inc :: Integer -> Integer In Haskell function application is denoted using a space f a b + c*d Function application has higher priority than all other operators f a + b -> (f a) + b

7 Haskell - introduction
Mathematics f(x) f(x,y) f(g(x)) f(x,g(y)) f(x)*g(y) Haskell f x f x y f (g x) f x (g y) f x * g y

8 Naming requirements Function and argument names must begin with lower case letters: myFun fun1 arg_2 By convention, list arguments usually have an s suffix on their name: xs ns nss In a sequence of definitions, each definition must begin in precisely the same column a = 10 a = 10 b = b = 20 c = 30 c = 30 The layout rule avoids the needs for explicit syntax to indicate the grouping of definitions

9 2. Types and classes Every expression has a type t, which can be automatically calculated at compile time by type inference: e :: t :type calculates the type of an expression: :type not False not False :: Bool Basic types Bool Char String Int (fixed precision) Float Integer (arbitrary precision)

10 Types and classes List type – sequence of values of the same type:
[False,True,False] :: [Bool] Tuple type – a sequence of values of different types: (False, True) :: (Bool, Bool) (False,'a',True) :: (Bool, Char, Bool) The list type does not encode its size The type of a tuple encodes its size

11 Function types A function is a mapping from values of one type to values of another type: not :: Bool -> Bool isDigit :: Char -> Bool t1 -> t2 is the type of functions that map values of type t1 to values of type t2 add :: (Int,Int) -> Int add (x,y) = x = y zeroto :: Int -> Int zeroto n = [0..n]

12 Polymorphic types Polymorphic type expressions - describe families of types. [a] is the family of types consisting of, for every type a, the type of lists of a. Lists of integers (e.g. [1,2,3]), lists of characters (['a','b','c']), even lists of lists of integers, etc., are all members of this family. a – type variable Haskell has only universally quantified types

13 Polymorphic types Polymorphic types - some types are strictly more general than others in the sense that the set of values they denote is larger. The type [a] is more general than [Char]. The latter type can be derived from the former by a suitable substitution for a.

14 Polymorphic types With regard to this generalization ordering, Haskell's type system possesses two important properties: every well-typed expression is guaranteed to have a unique principal type the principal type can be inferred automatically

15 Polymorphic types An expression's or function's principal type is the least general type that, intuitively, contains all instances of the expression. head :: [a] -> a head (x:xs) = x For example, the principal type of head is [a]->a; [b]->a, a->a, or even a are correct types, but too general, whereas something like [Integer]->Integer is too specific. The existence of unique principal types is the hallmark feature of the Hindley-Milner type system, which forms the basis of the type systems of Haskell, ML, Miranda, and several other (mostly functional) languages.

16 Curried functions Functions with multiple arguments are also possible by returning functions as results: add :: Int -> (Int -> Int) add x y = x + y add takes an integer x and returns a function add x. In turn this function takes an integer y and returns the result x + y

17 Curried functions add and add_1 produce the same result but add_1 takes its two arguments at the same time, while add takes them one at a time. add_1 :: (Int,Int) -> Int add :: Int -> (Int -> Int) Functions that take their arguments one at a time are called curried functions, in honor of the work of Haskell Curry on such functions

18 Curried functions Functions with more than two arguments can be curried by returning nested functions. mult :: Int -> (Int -> (Int -> Int)) mult x y z = x*y*z mult takes an integer x and returns a function mult x, which in turn takes an integer y and returns a function mult x y, which finally takes an integer z and returns the result x*y*z

19 Curried functions Integer->Integer->Integer is equivalent to
add :: Integer -> Integer -> Integer add x y = x + y Integer->Integer->Integer is equivalent to Integer->(Integer->Integer); i.e. -> associates to the right.

20 Curried functions As a consequence it is natural for a function application to associate to the left mult x y z means ((mult x) y) z Unless tupling is explicitly required, all functions in Haskell are normally defined in curried form

21 Why currying? Curried functions are more flexible than functions on tuples because useful functions can often be made by partially applying a curried function add 1 :: Int -> Int inc = add 1 This is an example of the partial application of a curried function, and is one way that a function can be returned as a value.

22 Why currying? Pass a function as an argument.
The map function is a perfect example: map :: (a->b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x : map f xs This is an example of the partial application of a curried function, and is one way that a function can be returned as a value.

23 Polymorphic functions
A function is called polymorphic if its type contains one or more type variables length :: [a] -> Int head :: [a] -> a id :: a -> a Type variables must begin with lower case letters and are usually named a, b, c, etc. Type variables can be instantiated to different types in different circumstances length [False, True] length [1,2,3,4]

24 Overloaded functions A polymorphic function is called overloaded if its type contains one or more class constraints sum :: Num a => [a] -> a For any numeric type a, sum takes a list of values of type a and returns a value of type a Constrained type variables can be instantiated to any types that satisfy the constraint sum [1,2,3] sum [1.1, 2.2, 3.3] sum ['a', 'b', 'c'] ERROR

25 Type classes (==) :: Eq a => a -> a -> Bool
Haskell has a number of type classes, including: Num – Numeric types Eq – Equality types Ord – Ordered types (+) :: Num a => a -> a -> a (==) :: Eq a => a -> a -> Bool (<) :: Ord a => a -> a -> Bool

26 3. Defining functions Conditional expressions abs :: Int -> Int
abs n = if n >= 0 then n else -n signum :: Int -> Int signum n = if n < 0 then -1 else if n == 0 then 0 else 1 In Haskell, conditional expressions must always have an else branch, which avoids any possible ambiguity problems with nested conditionals

27 Guarded equations As an alternative to conditionals, functions can also be defined using guarded equations abs :: Int -> Int abs n | n >= = n | otherwise = -n Guarded equations can be used to make definitions involving multiple conditions easier to read: signum :: Int -> Int signum n | n < = -1 | n == = 0 | otherwise = 1

28 Pattern Matching Many functions have a particularly clear definition using pattern matching on their arguments not :: Bool -> Bool not False = True not True = False

29 Pattern Matching Functions may be defined in many different ways using pattern matching (&&) :: Bool -> Bool -> Bool True && True = True True && False = False False && True = False False && False = False can be defined more compactly by True && True = True _ && _ = False

30 Pattern Matching The following definition is more efficient
True && b = b False && _ = False Patterns are matched in order. For example the following definition returns False _ && _ = False True && True = True Patterns may not repeat variables. For example, the following definition gives an error b && b = b _ && _ = False ERROR

31 List patterns List – cons operator :
[1,2,3,4] means 1:(2:(3:(4:[]))) Functions on lists can be defined using x:xs patterns head :: [a] -> a head (x:_) = x tail :: [a] -> [a] tail (_:xs) = xs x:xs patterns must be parenthesised. For example, the following definition gives an error head x:_ = x ERROR

32 Integer patterns Functions on integers can be defined using n+k patterns, where n is an integer variable and k>0 is an integer constant : pred :: Int -> Int pred (n+1) = n n+k patterns only match integers k>0 pred 0 ERROR n+k patterns must be parenthesised because function application has priority over + pred n+1 = n ERROR

33 Lambda expressions Functions can be constructed without naming the functions by using lambda expressions lambda expressions can be used to give a formal meaning to functions using currying add x y = x+y means add = \x -> (\y -> x+y)

34 Lambda expressions odds n = map f [0..n-1] where f x = x*2 + 1
can be simplified to odds n = map (\x -> x*2 +1) [0..n-1]

35 Sections An operator written between its two arguments can be converted into a curried function written before its two arguments by using parentheses (+) This convention allows one of the arguments of the operator to be included in the parentheses (1+) 2 3 (+2) 1 3 In general, is an operator then functions of the form and are called sections.

36 Why sections ? Useful functions can sometimes be constructed in a simple way using sections. For example: (1+) - successor function (1/) – reciprocation function (*2) – doubling function (/2) – halving function

37 4. List comprehensions In mathematics, the comprehension notation can be used to construct new sets from old sets: {x2 | x  {1..5}} In Haskell, a similar comprehension notation can be used to construct new lists from old lists [x^2 | x <- [1..5]] [1,4,9,16,25] The expression x <- [1..5] is called a generator as it states how to generate values for x

38 List comprehensions Comprehensions can have multiple generators, separated by commas [(x,y) | x <- [1,2,3], y<- [4,5]] [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)] Changing the order of the generators changes the order of the elements in the final list: [(x,y) | y<- [4,5], x <- [1,2,3]] [(1,4),(2,4),(3,4),(1,5),(2,5),(3,5)] Multiple generators are like nested loops with later generators as more deeply nested loops whose variables change value more frequently

39 Dependent generators Later generators can depend on the variables that are introduced by earlier generators [(x,y) | x <- [1..3], y<- [x..3]] [(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)] Using a dependant generator we can define the library function that concatenates a list of lists: concat :: [[a]] -> [a] concat xss = [x | xs <- xss, x <- xs] concat [[1,2,3],[4,5],[6]] [1,2,3,4,5,6]

40 Guards List comprehensions can use guards to restrict the values produced by earlier generators [x | x <- [1..10], even x] [2,4,6,8,10] Using a guard we can define a function that maps a positive integer to its list of factors: factors :: Int -> Int factors n = [x | x <- [1..n], n `mod` x == 0] factors 15 [1,3,5,15]

41 Guards Using factors we can define a function that decides if a number is prime prime :: Int -> Bool prime n = factors n == [1,n] prime 15 False prime 7 True

42 Guards Using a guard we can now define a function that returns the list of all primes up to a given limit: primes :: Int -> [Int] primes n = [x | x <- [2..n], prime x] primes 40 [2,3,5,7,11,13,17,19,23,29,31,37]

43 The Zip function A useful library function is zip, which maps two lists to a list of pairs of their corresponding elements: zip :: [a] -> [b] -> [(a,b)] zip ['a','b','c'] [1,2,3] [('a',1),('b',2),('c',3)]

44 The Zip function Using zip we can define a function that returns the list of all pairs of adjacent elements from a list: pairs :: [a] -> [(a,a)] pairs xs = zip xs (tal xs) pairs [1,2,3,4] [(1,2),(2,3),(3,4)]

45 Is the list sorted? Using pairs we can define a function that decides if the elements in a list are sorted: sorted :: Ord a => [a] -> Bool sorted xs = and [x<= y | (x,y) <- pairs xs] sorted [1,2,3,4] True

46 Positions Using zip we can define a function that returns the list of all positions of a value in a list: positions :: Eq a => a - [a] -> [Int] positions x xs = [ i | (x',i) <- zip xs [0..n], x == x'] where n = length xs - 1 positions 0 [1,0,0,1,0,1,1,0] [1,2,4,7]

47 5. String comprehensions
Internally strings are represented as lists of characters: "abc" :: String ["a','b','c'] :: [Char] Because strngs are just special kinds of lists, any polymorphic function that operates on lists can also be applied to strings length "abc" 3 take 3 "abcde" "abc" zip "abc" [1,2,3,4] [('a',1),('b',2),('c',3)]

48 String comprehension List comprehension can be used to define functions on strings Ex function that counts the lower case letters in a string: lowers :: String -> Int lowers xs = length [x | x <- xs, isLower x] lowers "Haskell" 6

49 6. Recursive functions length :: [a] -> Int length [] = 0
length (_:xs) = 1 + length xs reverse :: [a] -> a reverse [] = [] reverse (x:xs) = reverse xs ++ [x]

50 Recursive functions zip :: [a] -> [b] -> [(a,b)] zip [ ] _ = [ ]
zip (x:xs) (y:ys) = (x,y) : zip xs ys (++) :: [a] -> [a] -> [a] [ ] ++ ys = ys (x:xs) ++ ys = x: (xs ++ ys) drop :: Int -> [a] -> [a] drop 0 xs = xs drop (n+1) [ ] = [ ] drop (n+1) (_:xs) = drop n xs

51 Recursive functions sort :: [Int] -> [Int] sort [ ] = [ ]
sort (x:xs) = sort smaller ++ [x] ++ sort larger where smaller = [a | a <- xs, a<= x] larger = [b | b <- xs, b>x] QUCKSORT Non-empty lists can be sorted by sorting the tail values <= the head sorting the tail values > the head and then appending the resulting lists on either side of the head value

52 High-order functions See file chapter7.ppt - Slides from:
Programming in Haskell, Graham Hutton, Cambridge University Press (January 15, 2007)


Download ppt "Functional Programming"

Similar presentations


Ads by Google