440 likes | 737 Views
Lazy Functional Programming in Haskell. H. Conrad Cunningham, Yi Liu, and Hui Xiong Software Architecture Research Group Computer and Information Science University of Mississippi. Programming Language Paradigms. Imperative languages have implicit states use commands to modify state
E N D
Lazy Functional Programming in Haskell H. Conrad Cunningham, Yi Liu, and Hui Xiong Software Architecture Research Group Computer and Information Science University of Mississippi
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, …
Orderly Expressions andDisorderly Statements Values ofxandydepend upon order of execution of statements x represents different values in different contexts
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
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
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
Quicksort Algorithm • If sequence is empty, then it is sorted • Take any element as pivot value • Partition rest of sequence into two parts • elements < pivot value • elements >= pivot value • Sort each part using Quicksort • Result is sorted part 1, followed by pivot, followed by sorted part 2
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 ); } }
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]
Types • Basic types • Bool • Int • Float • Double • Char • Structured types • lists [] • tuples ( , ) • user-defined types • functions ->
Definitions • Definitions name :: type e.g.: size :: Int size = 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)
Factorial Function fact1 :: Int -> Int fact1 n -- use guards on two equations | n == 0 = 1 | n > 0 = n * fact1 (n-1)
Another Factorial Function fact1 :: Int -> Int fact1 n -- use guards on two equations | n == 0 = 1 | n > 0 = n * fact1 (n-1) fact2 :: Int -> Int fact2 0 = 1 -- use pattern matching fact2 (n+1) = (n+1) * fact n fact1 and fact2 represent the same function
Yet Another Factorial Function fact2 :: Int -> Int fact2 0 = 1 fact2 (n+1) = (n+1) * fact n fact3 :: Int -> Int fact3 n = product [1..n] -- library functions fact3 differs slightly from fact1 and fact Consider fact2 (-1) and fact3 (-1)
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 ] givesexplicit list of elements, [] is nil list len :: [a] -> Int -- polymorphic list argument len [] = 0 -- nil list len (x:xs) = 1 + len xs -- non-nil list
List Append infixr 5 ++ -- infix operator -- note polymorphism (++) :: [a] -> [a] -> [a] [] ++ xs = xs -- infix pattern match (x:xs) ++ ys = x:(xs ++ ys)
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
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 def squareAll’ xs = map (\x -> x * x) xs -- anonymous function -- or lambda expression lengthAll xs = map length xs
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
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)
Yet Another Higher-Order Function sumlist :: [Int] -> Int sumlist [] = 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
Fold Right Abstract different binary operators to be applied foldr :: (a -> b -> b) -> b -> [a] -> b foldr f z [] = z -- binary op, identity, list foldr f z (x:xs) = f x (foldr f z xs) sumlist :: [Int] -> Int sumlist xs = foldr (+) 0 xs concat' :: [[a]] -> [a] concat' xss = foldr (++) [] xss
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))
Divide and Conquer Function fib :: Int -> Int fib 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
Currying and Partial Evaluation add :: (Int,Int) -> Int add (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)
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 ]
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,…], … ]
Sieve of Eratosthenes Algorithm • Generate list 2, 3, 4, … • Mark first element p of list as prime • Delete all multiples of p from list • 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
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, …
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
User-Defined Data Types data BinTree a = Empty | Node (BinTree a) a (BinTree a) height :: BinTree -> Int height Empty = 0 height (Node l v r) = max (height l) (height r) + 1 flatten :: BinTree a -> [a] -- in-order traversal flatten Empty = [] flatten (Node l v r) = flatten l ++ [v] ++ flatten r
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
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)
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
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.
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.