Post on 28-Dec-2015
Lazy Functional Programming in Haskell
H. Conrad Cunningham, Yi Liu, and Hui Xiong
Software Architecture Research GroupComputer and Information Science
University of Mississippi
2-Apr-2004 2
Programming Language Paradigms
• Imperative languages – have implicit states– use commands to modify state– express how something is computed– include C, Pascal, Ada, …
• Declarative languages– have no implicit states– use expressions that are evaluated– express what is to be computed– have different underlying models
• functions: Lisp (Pure), ML, Haskell, …spreadsheets? SQL?• relations (logic): Prolog (Pure) , Parlog, …
2-Apr-2004 3
Orderly Expressions andDisorderly Statements
{ x = 1; y = 2; z = 3 }
x = 3 * x + 2 * y + z
y = 5 * x – y + 2 * z
Values of x and y depend upon order of execution of statements
x represents different values in different contexts
2-Apr-2004 4
Why Use Functional Programming?
• Referential transparency– symbol always represents the same value– easy mathematical manipulation, parallel execution, etc.
• Expressive and concise notation• Higher-order functions
– take/return functions– powerful abstraction mechanisms
• Lazy evaluation– defer evaluation until result needed– new algorithmic approaches
2-Apr-2004 5
Why Teach/Learn FP and Haskell?
• Introduces new problem solving techniques• Improves ability to build and use higher-level
procedural and data abstractions• Helps instill a desire for elegance in design and
implementation• Increases comfort and skill in use of recursive
programs and data structures• Develops understanding of programming languages
features such as type systems• Introduces programs as mathematical objects in a
natural way
2-Apr-2004 6
Haskell and Hugs
• Haskell– Standard functional language for research – Work began in late 1980s– Web site: http://www.haskell.org
• Hugs – Interactive interpreter for Haskell 98– Download from:
http://www.haskell.org/hugs
2-Apr-2004 7
Quicksort Algorithm
• If sequence is empty, then it is sorted
• Take any element as pivot value
• Partition rest of sequence into two parts1. elements < pivot value
2. elements >= pivot value
• Sort each part using Quicksort
• Result is sorted part 1, followed by pivot, followed by sorted part 2
2-Apr-2004 8
Quicksort in C
qsort( a, lo, hi ) int a[ ], hi, lo; { int h, l, p, t; if (lo < hi) { l = lo; h = hi; p = a[hi]; do { while ((l < h) && (a[l] <= p)) l = l + 1; while ((h > l) && (a[h] >= p)) h = h – 1 ; if (l < h) { t = a[l]; a[l] = a[h]; a[h] = t; } } while (l < h); t = a[l]; a[l] = a[hi]; a[hi] = t; qsort( a, lo, l-1 ); qsort( a, l+1, hi ); } }
2-Apr-2004 9
Quicksort in Haskell
qsort :: [Int] -> [Int]qsort [] = [] qsort (x:xs) = qsort lt ++ [x] ++ qsort greq where lt = [y | y <- xs, y < x] greq = [y | y <- xs, y >= x]
2-Apr-2004 10
Types
• Basic types– Bool
– Int
– Float
– Double
– Char
• Structured types– lists [] – tuples ( , ) – user-defined types
– functions ->
2-Apr-2004 11
• Definitions name :: type
e.g.:size :: Intsize = 12 - 3
• Function definitions name :: t1 -> t2 -> … -> tk -> t
function name types of type of result arguments e.g.: exOr :: Bool -> Bool -> Bool exOr x y = (x || y) && not (x && y)
Definitions
2-Apr-2004 12
Factorial Function
fact1 :: Int -> Intfact1 n -- use guards on two equations | n == 0 = 1 | n > 0 = n * fact1 (n-1)
2-Apr-2004 13
Another Factorial Function
fact1 :: Int -> Intfact1 n -- use guards on two equations
| n == 0 = 1 | n > 0 = n * fact1 (n-1)
fact2 :: Int -> Intfact2 0 = 1 -- use pattern matchingfact2 (n+1) = (n+1) * fact n
fact1 and fact2 represent the same function
2-Apr-2004 14
Yet Another Factorial Function
fact2 :: Int -> Intfact2 0 = 1fact2 (n+1) = (n+1) * fact n
fact3 :: Int -> Intfact3 n = product [1..n] -- library functions
fact3 differs slightly from fact1 and factConsider fact2 (-1) and fact3 (-1)
2-Apr-2004 15
List Cons and Length
(x:xs) on right side of defining equation means to form new list with x as head element and xs as tail list – colon is “cons”
(x:xs) on left side of defining equation means to match a list with at least one element, x gets head element, xs gets tail list
[ x1, x2, x3 ] gives explicit list of elements, [] is nil list
len :: [a] -> Int -- polymorphic list argumentlen [] = 0 -- nil listlen (x:xs) = 1 + len xs -- non-nil list
2-Apr-2004 16
List Append
infixr 5 ++ -- infix operator
-- note polymorphism(++) :: [a] -> [a] -> [a][] ++ xs = xs -- infix pattern match(x:xs) ++ ys = x:(xs ++ ys)
2-Apr-2004 17
Abstraction: Higher-Order Functions
squareAll :: [Int] -> [Int]squareAll [] = []squareAll (x:xs) = (x * x) : squareAll xs
lengthAll :: [[a]] -> [Int]lengthAll [] = []lenghtAll (xs:xss) = (length xs) : lengthAll xss
2-Apr-2004 18
Map
Abstract different functions applied as function argument to a library function called map
map :: (a -> b) -> [a] -> [b]map f [] = []map f (x:xs) = (f x) : map f xs
squareAll xs = map sq xs where sq x = x * x -- local function defsquareAll’ xs = map (\x -> x * x) xs -- anonymous function -- or lambda expressionlengthAll xs = map length xs
2-Apr-2004 19
Another Higher-Order Function
getEven :: [Int] -> [Int]getEven [] = []getEven (x:xs) | even x = x : getEven xs | otherwise = getEven xs
doublePos :: [Int] -> [Int]doublePos [] = []doublePos (x:xs) | 0 < x = (2 * x) : doublePos xs | otherwise = doublePos xs
2-Apr-2004 20
Filter
filter :: (a -> Bool) -> [a] -> [a]filter _ [] = []filter p (x:xs) | p x = x : xs' | otherwise = xs' where xs' = filter p xs
getEven :: [Int] -> [Int]getEven xs = filter even xs -- use library function
doublePos :: [Int] -> [Int]doublePos xs = map dbl (filter pos xs) where dbl x = 2 * x pos x = (0 < x)
2-Apr-2004 21
Yet Another Higher-Order Function
sumlist :: [Int] -> Intsumlist [] = 0 -- nil list sumlist (x:xs) = x + sumlist xs -- non-nil
concat' :: [[a]] -> [a]concat' [] = [] -- nil list of lists concat' (xs:xss) = xs ++ concat' xss -- non-nil
2-Apr-2004 22
Fold Right
Abstract different binary operators to be applied
foldr :: (a -> b -> b) -> b -> [a] -> b foldr f z [] = z -- binary op, identity, listfoldr f z (x:xs) = f x (foldr f z xs)
sumlist :: [Int] -> Intsumlist xs = foldr (+) 0 xs
concat' :: [[a]] -> [a]concat' xss = foldr (++) [] xss
2-Apr-2004 23
Divide and Conquer Function
divideAndConquer :: (a -> Bool) -- trivial -> (a -> b) -- simplySolve -> (a -> [a]) -- decompose -> (a -> [b] -> b) -- combineSolutions -> a -- problem -> b
divideAndConquer trivial simplySolve decompose combineSolutions problem = solve problem where solve p | trivial p = simplySolve p | otherwise = combineSolutions p map solve (decompose p))
2-Apr-2004 24
Divide and Conquer Function
fib :: Int -> Intfib n = divideAndConquer trivial simplySolve decompose combineSolutions n where trivial 0 = True trivial 1 = True trivial (m+2)= False simplySolve 0 = 0 simplySolve 1 = 1 decompose m = [m-1,m-2] combineSolutions _ [x,y] = x + y
2-Apr-2004 25
Currying and Partial Evaluation
add :: (Int,Int) -> Intadd (x,y) = x + y
? add(3,4) => 7? add (3, ) => error
add’ takes one argument and returns a function
Takes advantage of Currying
add' :: Int->(Int->Int) add' x y = x + y
? add’ 3 4 => 7 ? add’ 3
(add’ 3) :: Int -> Int (add’ 3) x = 3 + x
((+) 3)
2-Apr-2004 26
Using Partial Evaluation
doublePos :: [Int] -> [Int] doublePos xs = map ((*) 2) (filter ((<) 0) xs)
• Using operator section notation
doublePos xs = map (*2) (filter (0<) xs)
• Using list comprehension notation
doublePos xs = [ 2*x | x <- xs, 0< x ]
2-Apr-2004 27
Lazy Evaluation Do not evaluate an expression unless its value is needed
iterate :: (a -> a) -> a -> [a]iterate f x = x : iterate f (f x)
interate (*2) 1 => [1, 2, 4, 8, 16, …]
powertables :: [[Int]]powertables = [ iterate (*n) 1 | n <- [2..] ]
powertables => [ [1, 2, 4, 8,…], [1, 3, 9, 27,…], [1, 4,16, 64,…], [1, 5, 25,125,…], … ]
2-Apr-2004 28
Sieve of Eratosthenes Algorithm
1. Generate list 2, 3, 4, … 2. Mark first element p of list as prime3. Delete all multiples of p from list 4. Return to step 2
primes :: [Int]primes = map head (iterate sieve [2..])
sieve (p:xs) = [x | x <- xs, x `mod` p /= 0 ]
takewhile (< 10000) primes
2-Apr-2004 29
Hamming’s Problem
Produce the list of integers• increasing order (hence no duplicate values)• begins with 1• if n is in list, then so is 2*n, 3*n, and 5*n• list contains no other elements
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, …
2-Apr-2004 30
Hamming Program
ham :: [Int]ham = 1 : merge3 [ 2*n | n <- ham ]
[ 3*n | n <- ham ] [ 5*n | n <- ham ]
merge3 :: Ord a => [a] -> [a] -> [a] -> [a]merge3 xs ys zs = merge2 xs (merge2 ys zs)
merge2 xs'@(x:xs) ys'@(y:ys) | x < y = x : merge2 xs ys' | x > y = y : merge2 xs' ys | otherwise = x : merge2 xs ys
2-Apr-2004 31
User-Defined Data Types
data BinTree a = Empty | Node (BinTree a) a (BinTree a)
height :: BinTree -> Intheight Empty = 0height (Node l v r) = max (height l) (height r) + 1
flatten :: BinTree a -> [a] -- in-order traversalflatten Empty = []flatten (Node l v r) = flatten l ++ [v] ++ flatten r
2-Apr-2004 32
Other Important Haskell Features
• Type inferencing• Type classes and overloading• Rich set of built-in types• Extensive libraries• Modules• Monads for purely functional I/O and other actions
2-Apr-2004 33
Other Important FP Topics
• Problem solving and program design techniques• Abstract data types• Proofs about functional program properties• Program synthesis• Reduction models• Parallel execution (dataflow interpretation)
2-Apr-2004 34
Haskell-based Textbooks
• Simon Thompson. Haskell: The Craft of Functional Programming, Addison Wesley, 1999.
• Richard Bird. Introduction to Functional Programming Using Haskell, second edition, Prentice Hall Europe, 1998.
• Paul Hudak. The Haskell School of Expression, Cambridge University Press, 2000.
• H. C. Cunningham. Notes on Functional Programming with Gofer, Technical Report UMCIS-1995-01, University of Mississippi, Department of Computer and Information Science, Revised January 1997. http://www.cs.olemiss.edu/~hcc/reports/gofer_notes.pdf
2-Apr-2004 35
Other FP Textbooks Of Interest
• Fethi Rabhi and Guy Lapalme. Algorithms: A Functional Approach, Addison Wesley, 1999.
• Chris Okasaki. Purely Functional Data Structures, Cambridge University Press, 1998.
2-Apr-2004 36
Acknowledgements
• Individuals who helped me get started teaching lazy functional programming in the early 1990s.
• Mark Jones and others who implemented the Hugs interpreter.
• More than 200 students who have been in my 10 classes on functional programming in the past 14 years.
• Acxiom Corporation for funding some of my recent work on software architecture.