0 PROGRAMMING IN HASKELL Chapter 9 - Higher-Order Functions, Functional Parsers

Download 0 PROGRAMMING IN HASKELL Chapter 9 - Higher-Order Functions, Functional Parsers

Post on 13-Jan-2016




3 download

Embed Size (px)


<ul><li><p>*PROGRAMMING IN HASKELLChapter 9 - Higher-Order Functions,Functional Parsers</p></li><li><p>Introduction*A function is called higher-order if it takes a function as an argument or returns a function as a result.twice :: (a a) a atwice f x = f (f x)twice is higher-order because ittakes a function as its first argument.</p></li><li><p>Why Are They Useful?*Common programming idioms can be encoded as functions within the language itself.</p><p>Domain specific languages can be defined as collections of higher-order functions.</p><p>Algebraic properties of higher-order functions can be used to reason about programs.</p></li><li><p>The Map Function*The higher-order library function called map applies a function to every element of a list.map :: (a b) [a] [b]For example:&gt; map (+1) [1,3,5,7]</p><p>[2,4,6,8]</p></li><li><p>*Alternatively, for the purposes of proofs, the map function can also be defined using recursion: The map function can be defined in a particularly simple manner using a list comprehension:map f xs = [f x | x xs]map f [] = []map f (x:xs) = f x : map f xs</p></li><li><p>The Filter Function*The higher-order library function filter selects every element from a list that satisfies a predicate.filter :: (a Bool) [a] [a]For example:&gt; filter even [1..10]</p><p>[2,4,6,8,10]</p></li><li><p>*Alternatively, it can be defined using recursion:Filter can be defined using a list comprehension:filter p xs = [x | x xs, p x]filter p [] = []filter p (x:xs) | p x = x : filter p xs | otherwise = filter p xs</p></li><li><p>The Foldr Function*A number of functions on lists can be defined using the following simple pattern of recursion:f [] = vf (x:xs) = x f xsf maps the empty list to some value v, and any non-empty list to some function applied to its head and f of its tail.</p></li><li><p>*For example:sum [] = 0sum (x:xs) = x + sum xsand [] = Trueand (x:xs) = x &amp;&amp; and xsproduct [] = 1product (x:xs) = x * product xsv = 0 = +v = 1 = *v = True = &amp;&amp;</p></li><li><p>*The higher-order library function foldr (fold right) encapsulates this simple pattern of recursion, with the function and the value v as arguments.</p><p>For example:sum = foldr (+) 0</p><p>product = foldr (*) 1</p><p>or = foldr (||) False </p><p>and = foldr (&amp;&amp;) True</p></li><li><p>*Foldr itself can be defined using recursion:foldr :: (a b b) b [a] bfoldr f v [] = vfoldr f v (x:xs) = f x (foldr f v xs)However, it is best to think of foldr non-recursively, as simultaneously replacing each (:) in a list by a given function, and [] by a given value.</p></li><li><p>*sum [1,2,3]For example:Replace each (:)by (+) and [] by 0.</p></li><li><p>*product [1,2,3]For example:Replace each (:)by (*) and [] by 1.</p></li><li><p>Other Foldr Examples*Even though foldr encapsulates a simple pattern of recursion, it can be used to define many more functions than might first be expected.</p><p>Recall the length function:length :: [a] Intlength [] = 0length (_:xs) = 1 + length xs</p></li><li><p>*length [1,2,3]Replace each (:) by _ n 1+n and [] by 0.For example:</p></li><li><p>*Now recall the reverse function:reverse [] = []reverse (x:xs) = reverse xs ++ [x]reverse [1,2,3]For example:Replace each (:) by x xs xs ++ [x] and [] by [].</p></li><li><p>*Hence, we have:reverse = foldr (x xs xs ++ [x]) []Finally, we note that the append function (++) has a particularly compact definition using foldr:(++ ys) = foldr (:) ysReplace each (:) by (:) and [] by ys.</p></li><li><p>Why Is Foldr Useful?*Some recursive functions on lists, such as sum, are simpler to define using foldr.</p><p>Properties of functions defined using foldr can be proved using algebraic properties of foldr, such as fusion and the banana split rule.</p><p>Advanced program optimisations can be simpler if foldr is used in place of explicit recursion.</p></li><li><p>Other Library Functions*The library function (.) returns the composition of two functions as a single function.(.) :: (b c) (a b) (a c)f . g = x f (g x)For example:odd :: Int Boolodd = not . even</p></li><li><p>*The library function all decides if every element of a list satisfies a given predicate.all :: (a Bool) [a] Boolall p xs = and [p x | x xs]For example:&gt; all even [2,4,6,8,10]</p><p>True</p></li><li><p>*Dually, the library function any decides if at leastone element of a list satisfies a predicate.any :: (a Bool) [a] Boolany p xs = or [p x | x xs]For example:&gt; any isSpace "abc def"</p><p>True</p></li><li><p>*The library function takeWhile selects elements from a list while a predicate holds of all the elements.takeWhile :: (a Bool) [a] [a]takeWhile p [] = []takeWhile p (x:xs) | p x = x : takeWhile p xs | otherwise = []For example:&gt; takeWhile isAlpha "abc def"</p><p>"abc"</p></li><li><p>*Dually, the function dropWhile removes elements while a predicate holds of all the elements.dropWhile :: (a Bool) [a] [a]dropWhile p [] = []dropWhile p (x:xs) | p x = dropWhile p xs | otherwise = x:xsFor example:&gt; dropWhile isSpace " abc"</p><p>"abc"</p></li><li><p>Exercises*</p></li><li><p>What is a Parser?*A parser is a program that analyses a piece of text to determine its syntactic structure.23+4</p></li><li><p>Where Are They Used?*Almost every real life program uses some form of parser to pre-process its input.</p></li><li><p>The Parser Type*In a functional language such as Haskell, parsers can naturally be viewed as functions.type Parser = String TreeA parser is a function that takes a string and returns some form of tree.</p></li><li><p>*However, a parser might not require all of its input string, so we also return any unused input:type Parser = String (Tree,String)A string might be parsable in many ways, including none, so we generalize to a list of results:type Parser = String [(Tree,String)]</p></li><li><p>*Finally, a parser might not always produce a tree, so we generalize to a value of any type:type Parser a = String [(a,String)]Note:For simplicity, we will only consider parsers that either fail and return the empty list of results, or succeed and return a singleton list.</p></li><li><p>Basic Parsers*The parser item fails if the input is empty, and consumes the first character otherwise:item :: Parser Charitem = inp case inp of [] [] (x:xs) [(x,xs)] </p></li><li><p>*The parser failure always fails:failure :: Parser afailure = inp []The parser return v always succeeds, returning the value v without consuming any input:return :: a Parser areturn v = inp [(v,inp)]</p></li><li><p>*The parser p +++ q behaves as the parser p if it succeeds, and as the parser q otherwise:(+++) :: Parser a Parser a Parser ap +++ q = inp case p inp of [] parse q inp [(v,out)] [(v,out)]The function parse applies a parser to a string:parse :: Parser a String [(a,String)]parse p inp = p inp</p></li><li><p>Examples*% hugs Parsing</p><p>&gt; parse item ""[] </p><p>&gt; parse item "abc"[('a',"bc")]The behavior of the five parsing primitives can be illustrated with some simple examples:</p></li><li><p>*&gt; parse failure "abc"[]</p><p>&gt; parse (return 1) "abc"[(1,"abc")]</p><p>&gt; parse (item +++ return 'd') "abc"[('a',"bc")]</p><p>&gt; parse (failure +++ return 'd') "abc"[('d',"abc")]</p></li><li><p>*Note:The library file Parsing is available on the web from the Programming in Haskell home page. </p><p>For technical reasons, the first failure example actually gives an error concerning types, but this does not occur in non-trivial examples.</p><p>The Parser type is a monad, a mathematical structure that has proved useful for modeling many different kinds of computations.</p></li><li><p>Sequencing*A sequence of parsers can be combined as a single composite parser using the keyword do.</p><p>For example:p :: Parser (Char,Char)p = do x item item y item return (x,y)</p></li><li><p>*Note:Each parser must begin in precisely the same column. That is, the layout rule applies.</p><p>The values returned by intermediate parsers are discarded by default, but if required can be named using the operator.</p><p>The value returned by the last parser is the value returned by the sequence as a whole.</p></li><li><p>*If any parser in a sequence of parsers fails, then the sequence as a whole fails. For example:&gt; parse p "abcdef"[((a,c),"def")]</p><p>&gt; parse p "ab"[]The do notation is not specific to the Parser type, but can be used with any monadic type.</p></li><li><p>Derived Primitives*sat :: (Char Bool) Parser Charsat p = do x item if p x then return x else failureParsing a character that satisfies a predicate:</p></li><li><p>*digit :: Parser Chardigit = sat isDigit</p><p>char :: Char Parser Charchar x = sat (x ==)Parsing a digit and specific characters:Applying a parser zero or more times:many :: Parser a Parser [a]many p = many1 p +++ return []</p></li><li><p>*many1 :: Parser a -&gt; Parser [a]many1 p = do v p vs many p return (v:vs)Applying a parser one or more times:Parsing a specific string of characters:string :: String Parser Stringstring [] = return []string (x:xs) = do char x string xs return (x:xs)</p></li><li><p>Example*We can now define a parser that consumes a list of one or more digits from a string:p :: Parser Stringp = do char '[' d digit ds many (do char ',' digit) char ']' return (d:ds)</p></li><li><p>*For example:&gt; parse p "[1,2,3,4]"[("1234","")]</p><p>&gt; parse p "[1,2,3,4"[]Note:More sophisticated parsing libraries can indicate and/or recover from errors in the input string.</p></li><li><p>Arithmetic Expressions*Consider a simple form of expressions built up from single digits using the operations of addition + and multiplication *, together with parentheses.</p><p>We also assume that:* and + associate to the right;</p><p>* has higher priority than +.</p></li><li><p>*Formally, the syntax of such expressions is defined by the following context free grammar:expr term '+' expr term term factor '*' term factor</p><p>factor digit '(' expr ')</p><p>digit '0' '1' '9' </p></li><li><p>*However, for reasons of efficiency, it is important to factorise the rules for expr and term:expr term ('+' expr )</p><p>term factor ('*' term )Note: The symbol denotes the empty string.</p></li><li><p>*It is now easy to translate the grammar into a parser that evaluates expressions, by simply rewriting the grammar rules using the parsing primitives.</p><p>That is, we have:expr :: Parser Intexpr = do t term do char '+' e expr return (t + e) +++ return t</p></li><li><p>*factor :: Parser Intfactor = do d digit return (digitToInt d) +++ do char '(' e expr char ')' return eterm :: Parser Intterm = do f factor do char '*' t term return (f * t) +++ return f </p></li><li><p>*Finally, if we defineeval :: String Inteval xs = fst (head (parse expr xs))then we try out some examples:&gt; eval "2*3+4"10</p><p>&gt; eval "2*(3+4)"14</p></li><li><p>Exercises*expr term ('+' expr '-' expr )</p><p>term factor ('*' term '/' term )</p><p>*</p></li></ul>