COSC 4 P 41 Functional Programming Abstract data
COSC 4 P 41 – Functional Programming Abstract data types (ADTs) An ADT is a data type together with some functions to manipulate elements of this data type. It usually consists of 1. a signature (interface), between the user and the implementor, 2. an implementation which is (completely) hidden to the user. Example: A calculator for numerical expressions. data Expr = Lit Int | IVar | Let Var Expr | Expr : +: Expr | Expr : -: Expr | Expr : *: Expr | Expr : : Expr © M. Winter 7. 1
COSC 4 P 41 – Functional Programming ADTs (cont’d) eval eval : : Expr -> Store -> (Lit n) store (IVar v) store (Let v e 1 e 2) store (e 1 : +: e 2) store (e 1 : -: e 2) store (e 1 : *: e 2) store (e 1 : : e 2) store Int = n = value store v = eval e 2 (update store v (eval e 1 store)) = eval e 1 store + eval e 2 store = eval e 1 store - eval e 2 store = eval e 1 store * eval e 2 store = eval e 1 store `div` eval e 2 store execute : : Expr -> Int execute e = eval e initial © M. Winter 7. 2
COSC 4 P 41 – Functional Programming ADT Store USER initial : : Store value : : Store -> Var -> Int update : : Store -> Var -> Int -> Store IMPLEMENTOR The interface of Store consists of the functions / values 1. initial 2. value 3. update The implementation is hidden and can be exchanged. © M. Winter 7. 3
COSC 4 P 41 – Functional Programming ADT Store (1 st implementation) module Store(Store, initial, value, update) where newtype Store = Sto [(Var, Int)] initial : : Store initial = Sto [] value : : Store -> Var -> Int value (Sto []) v = 0 value (Sto ((v', n): sto)) v | v == v' = n | otherwise = value (Sto sto) v update : : Store -> Var -> Int -> Store update (Sto sto) v n = Sto ((v, n): sto) © M. Winter 7. 4
COSC 4 P 41 – Functional Programming ADT Store (2 nd implementation) module Store(Store, initial, value, update) where newtype Store = Sto (Var -> Int) initial : : Store initial = Sto (v -> 0) value : : Store -> Var -> Int value (Sto sto) v = sto v update : : Store -> Var -> Int -> Store update (Sto sto) v n = Sto (w -> if v==w then n else sto w) © M. Winter 7. 5
COSC 4 P 41 – Functional Programming Type classes We can declare ADTs as belonging to particular type classes. instance Eq Store where (Sto sto 1) == (Sto sto 2) = sto 1==sto 2 instance Show Store where show (Sto sto) = show sto Note, that once declared, these instances cannot be hidden, so that even though they are not named in the export list, the functions over Store which are defined by means of these instance declarations will be available whenever the module Store is imported. © M. Winter 7. 6
COSC 4 P 41 – Functional Programming ADT Queue The interface for the ADT Queue: USER type Queue a empty. Q : : Queue a is. Empty. Q : : Queue a - > Bool add. Q : : a -> Queue a rem. Q : : Queue a -> (a, Queue a) IMPLEMENTOR © M. Winter 7. 7
COSC 4 P 41 – Functional Programming ADT Queue (1 st implementation) module Queue(Queue, empty. Q, is. Empty. Q, add. Q, rem. Q) where newtype Queue a = Qu [a] empty. Q : : Queue a empty. Q = Qu [] is. Empty. Q : : Queue a -> Bool is. Empty. Q (Qu xs) = null xs add. Q : : a -> Queue a add. Q x (Qu xs) = Qu (xs++[x]) rem. Q | | : : Queue a -> (a, Queue a) q@(Qu xs) not (is. Empty. Q q) = (head xs, Qu (tail xs)) otherwise = error ”rem. Q” © M. Winter 7. 8
COSC 4 P 41 – Functional Programming Properties of the implementation One characteristic of this implementation is • add. Q is ‘expensive’, • rem. Q is ‘cheap’. Alternative: add. Q x (Qu xs) = Qu (x: xs) rem. Q q@(Qu xs) | not (is. Empty. Q q) | otherwise = (last xs, Qu (init xs)) = error ”rem. Q” But now we have: • add. Q is ‘cheap’, • rem. Q is ‘expensive’. © M. Winter 7. 9
COSC 4 P 41 – Functional Programming ADT Queue (2 nd implementation) module Queue(Queue, empty. Q, is. Empty. Q, add. Q, rem. Q) where data Queue a = Qu [a] empty. Q : : Queue a empty. Q = Qu [] [] is. Empty. Q : : Queue a -> Bool is. Empty. Q (Qu [] []) = True is. Empty. Q _ = False add. Q : : a -> Queue a add. Q x (Qu xs ys) = Qu xs (x: ys) rem. Q : : Queue a -> (a, Queue a) (Qu (x: xs) ys) = (x, Qu xs ys) (Qu [] (y: ys)) = rem. Q (Qu (reverse (y: ys)) []) (Qu [] []) = error ”rem. Q” © M. Winter 7. 10
COSC 4 P 41 – Functional Programming Why is there no length. Q function? Consider the following implementation of the function length. Q computing the length of a queue: length. Q : : Queue a -> Int length. Q q | is. Empty q = 0 | otherwise = 1 + (length. Q. snd. rem. Q) q The definition of length. Q is independent of the implementation, and so would not have to be reimplemented if the implementation of the type Queue a is changed. This is a good reason for leaving length. Q out of the signature. © M. Winter 7. 11
COSC 4 P 41 – Functional Programming Further examples of ADTs • • • the type Tree a of binary trees whose elements are of type a, binary search trees as an extension of binary trees whose elements are ordered, the type of sets, the type of relations between a and b, the type of a graph with node from a (special case of a relation). © M. Winter 7. 12
COSC 4 P 41 – Functional Programming Parsing Consider again the following data type for expressions: data Expr = Lit Int | IVar | Let Var Expr | Expr : +: Expr | Expr : -: Expr | Expr : *: Expr | Expr : : Expr The class Read can be used to generate elements of type Expr given a string representation. Notice, that the derived read function would accept strings as ”Lit 4 : : Lit 2” rather than the natural representation ” 4 2”. © M. Winter 7. 13
COSC 4 P 41 – Functional Programming A type for parsers First attempt: type Parse 1 a b = [a] -> b Suppose that bracket and number are parsers of this type which recognize brackets and numbers: bracket ”(xyz” number ” 234” bracket ” 234” ‘(‘ 2 or 234 no result? Second attempt: type Parse 2 a b = [a] -> [b] bracket ”(xyz” number ” 234” bracket ” 234” © M. Winter [‘(‘] [2, 234] [] 7. 14
COSC 4 P 41 – Functional Programming A type for parsers (cont’d) Final attempt: type Parse a b = [a] -> [(b, [a])] bracket ”(xyz” number ” 234” bracket ” 234” © M. Winter [(‘(‘, ”xyz”)] [(2, ” 34”), (23, ” 4”), (234, ””)] [] 7. 15
COSC 4 P 41 – Functional Programming Some basic parsers none : : Parse a b none inp = [] succeed : : b -> Parse a b succeed val inp = [(val, inp)] token : : Eq a => a -> Parse a a token t (x: xs) | t==x = [(t, xs)] | otherwise = [] token t [] = [] spot : : (a -> Bool) -> Parse a a spot p (x: xs) | p x = [(x, xs)] | otherwise = [] spot p [] = [] © M. Winter 7. 16
COSC 4 P 41 – Functional Programming Some basic parsers (cont’d) Some examples: bracket : : Parse Char bracket = token ‘(‘ bracket ”(xyz” [(‘(‘, ”xyz”)] dig : : Parse Char dig = spot is. Digit dig ” 23 df” © M. Winter [(‘ 2‘, ” 3 df”)] 7. 17
COSC 4 P 41 – Functional Programming Combining Parsers alt p 1 p 2 recognizes anything recognized by p 1 or by p 2. alt : : Parse a b -> Parse a b alt p 1 p 2 inp = p 1 inp ++ p 2 inp (bracket `alt` dig) ” 234” Apply one parser then the second to the result(s) of the first. infixr 5 >*> (>*>) : : Parse a b -> Parse a c -> Parse a (b, c) (>*>) p 1 p 2 inp = [((y, z), rem 2) | (y, rem 1) <- p 1 inp , (z, rem 2) (bracket >*> dig) ”(234” © M. Winter <- p 2 rem 1 ] [((‘(‘, ‘ 2‘), ” 34”)] 7. 18
COSC 4 P 41 – Functional Programming Combining Parsers (cont’d) Transform the results of the parses according to the function. build : : Parse a b -> (b -> c) -> Parse a c build p f inp = [ (f x, rem) | (x, rem) <- p inp ] (dig `build` char 2 int) ” 23 df” [(2, ” 3 df”)] infixr 5. *> (. *>) : : Parse a b -> Parse a c p 1. *> p 2 = (p 1 >*> p 2) `build` snd infixr 5 >*. (>*. ) : : Parse a b -> Parse a c -> Parse a b p 1 >*. p 2 = (p 1 >*> p 2) `build` fst © M. Winter 7. 19
COSC 4 P 41 – Functional Programming Combining Parsers (cont’d) Recognize a list of objects. list : : Parse a b -> Parse a [b] list p = (succeed []) `alt` ((p >*> list p) `build` convert) where convert = uncurry (: ) list dig ” 234(” [(””, ” 234(”), (” 2”, ” 34(”), (” 23”, ” 4(”), (” 234”, ”(”)] string. Token : : String -> Parse Char String string. Token s 1 s 2 = [ (a, b) | (a, b) <- lex s 2, a==s 1] © M. Winter 7. 20
COSC 4 P 41 – Functional Programming Parsing expressions Grammar for expressions: Expr = Int | Char | let Char = Expr in Expr | (Expr + Expr) | (Expr - Expr) | (Expr * Expr) | (Expr div Expr) Example: (4 + 3) div 2 © M. Winter 7. 21
COSC 4 P 41 – Functional Programming Parsing expressions parse. Var = spot (x -> 'a' <= x && x <= 'z') parser = (reads `build` Lit) `alt` (parse. Var `build` IVar) `alt` ((string. Token "let". *> parse. Var >*> string. Token "=". *> parser >*> string. Token "in". *> parser) `build` ((v, (e 1, e 2)) -> Let v e 1 e 2)) `alt` ((string. Token "(". *> parser >*> string. Token "+". *> parser >*. string. Token ")") `build` (uncurry (: +: ))) `alt` ((string. Token "(". *> parser >*> string. Token "-". *> parser >*. string. Token ")") `build` (uncurry (: -: ))) `alt` ((string. Token "(". *> parser >*> string. Token "*". *> parser >*. string. Token ")") `build` (uncurry (: *: ))) `alt` ((string. Token "(". *> parser >*> string. Token "div". *> parser >*. string. Token ")") `build` (uncurry (: : ))) © M. Winter 7. 22
COSC 4 P 41 – Functional Programming Parsing expression (cont’d) parse str = if null p then error ”Parse error: no parse” else if snd e /= ”” then error ”Parse error: unexpected end of input” else fst e where p = parser str e = head p instance Read Expr where reads. Prec n = parser © M. Winter 7. 23
COSC 4 P 41 – Functional Programming Further considerations Alternative grammar for expressions: Expr = Int | Char | let Char = Expr in Expr | (Expr) | Expr + Expr | Expr - Expr | Expr * Expr | Expr div Expr A similar translation into a Haskell program does not work !!! Reason: the grammar above is left-recursive. © M. Winter 7. 24
COSC 4 P 41 – Functional Programming Further considerations (cont’d) Solution: Modify grammar as follows: Expr = Expr 1 | Expr 1 + Expr | Expr 1 - Expr | Expr 1 * Expr | Expr 1 div Expr 1 | | | © M. Winter = Int Char let Char = Expr in Expr (Expr) 7. 25
COSC 4 P 41 – Functional Programming Using Parsec parser = do { n <- read 2 Parsec; return $ Lit n } <|> … <|> do { strings “(”; -- function in Basics. hs -- function in Bascics. hs -- Parsec only defines -- string : : String ->. . . t 1 <- parser; spaces; strings “-”; t 2 <- parser; spaces; strings “)”; return $ t 1 : -: t 2 } © M. Winter 7. 26
- Slides: 26