[FLOLAC'14][scm] Functional Programming Using Haskell

Download [FLOLAC'14][scm] Functional Programming Using Haskell

Post on 28-Aug-2014

449 views

Category:

Software

1 download

Embed Size (px)

DESCRIPTION

Instructed by Shin-Cheng Mu on 2014 Formosan Summer School of Logic, Language and Computation

TRANSCRIPT

<ul><li> Introduction to Functional Programming Using Haskell Shin-Cheng Mu Acadmia Sinica FLOLAC 2014 1 / 85 </li> <li> A Quick Introduction to Haskell We will be using the Glasgow Haskell Compiler (GHC). A Haskell compiler written in Haskell, with an interpreter that both interprets and runs compiled code. Installation: the Haskell Platform: http://hackage.haskell.org/platform/ 2 / 85 </li> <li> Function Denition A function denition consists of a type declaration, and the denition of its body: square :: Int Int square x = x x , smaller :: Int Int Int smaller x y = if x y then x else y . The GHCi interpreter evaluates expressions in the loaded context: ? square 3768 14197824 ? square (smaller 5 (3 + 4)) 25 3 / 85 </li> <li> Evaluation One possible sequence of evaluating (simplifying, or reducing) square (3 + 4): square (3 + 4) 4 / 85 </li> <li> Evaluation One possible sequence of evaluating (simplifying, or reducing) square (3 + 4): square (3 + 4) = { denition of + } square 7 4 / 85 </li> <li> Evaluation One possible sequence of evaluating (simplifying, or reducing) square (3 + 4): square (3 + 4) = { denition of + } square 7 = { denition of square } 7 7 4 / 85 </li> <li> Evaluation One possible sequence of evaluating (simplifying, or reducing) square (3 + 4): square (3 + 4) = { denition of + } square 7 = { denition of square } 7 7 = { denition of } 49 . 4 / 85 </li> <li> Another Evaluation Sequence Another possible reduction sequence: square (3 + 4) 5 / 85 </li> <li> Another Evaluation Sequence Another possible reduction sequence: square (3 + 4) = { denition of square } (3 + 4) (3 + 4) 5 / 85 </li> <li> Another Evaluation Sequence Another possible reduction sequence: square (3 + 4) = { denition of square } (3 + 4) (3 + 4) = { denition of + } 7 (3 + 4) 5 / 85 </li> <li> Another Evaluation Sequence Another possible reduction sequence: square (3 + 4) = { denition of square } (3 + 4) (3 + 4) = { denition of + } 7 (3 + 4) = { denition of + } 7 7 5 / 85 </li> <li> Another Evaluation Sequence Another possible reduction sequence: square (3 + 4) = { denition of square } (3 + 4) (3 + 4) = { denition of + } 7 (3 + 4) = { denition of + } 7 7 = { denition of } 49 . In this sequence the rule for square is applied rst. The nal result stays the same. Do dierent evaluations orders always yield the same thing? 5 / 85 </li> <li> A Non-terminating Reduction Consider the following program: innity :: Int innity = innity + 1 . Try evaluating square innity. square innity = { denition of square } innity innity = { denition of innity } (innity + 1) innity = ((innity + 1) + 1) innity . . . Some expressions have a terminating reduction sequence, some do not. This is related to one of the central issues in this course. 6 / 85 </li> <li> And, a Terminating Reduction In the previous case, the reduction does not terminate because, by rule, to evaluate x + y we have to know what x is. Consider the following program: three :: Int Int three x = 3 . Try evaluating three innity. 7 / 85 </li> <li> And, a Terminating Reduction If we start with simplifying three: three innity = { denition of three } 3 . It terminates because three x need not know what x is. Haskell uses a peculiar reduction strategy called lazy evaluation that evaluates an expression only when it really needs to. It allows more program to terminate (while causing lots of other troubles for some people). We will not go into this feature in this course. For now, it is sucient to know that some expression terminate, some do not, and it is in general good to know that an expression may terminate. 8 / 85 </li> <li> Mathematical Functions Mathematically, a function is a mapping between arguments and results. A function f :: A B maps each element in A to a unique element in B. In contrast, C functions are not mathematical functions: int y = 1; int f (x:int) { return ((y++) * x); } Functions in Haskell have no such side-eects: (unconstrained) assignments, IO, etc. Why removing these useful features? We will talk about that later in this course. 9 / 85 </li> <li> Curried Functions Consider again the function smaller: smaller :: Int Int Int smaller x y = if x y then x else y We sometimes informally call it a function taking two arguments. Usage: smaller 3 4. Strictly speaking, however, smaller is a function returning a function. The type should be bracketed as Int (Int Int). 10 / 85 </li> <li> Precedence and Association In a sense, all Haskell functions takes exactly one argument. Such functions are often called curried. Type: a b c = a (b c), not (a b) c. Application: f x y = (f x) y, not f (x y). smaller 3 4 means (smaller 3) 4. square square 3 means (square square) 3, which results in a type error. Function application binds tighter than inx operators. E.g. square 3 + 4 means (square 3) + 4. 11 / 85 </li> <li> Why Currying? It exposes more chances to reuse a function, since it can be partially applied. twice :: (a a) (a a) twice f x = f (f x) , quad :: Int Int quad = twice square . Try evaluating quad 3: quad 3 = twice square 3 = square (square 3) = . . . 12 / 85 </li> <li> Sectioning Inx operators are curried too. The operator (+) may have type Int Int Int. Inx operator can be partially applied too. (x ) y = x y , ( y) x = x y . (1 +) :: Int Int increments its argument by one. (1.0 /) :: Float Float is the reciprocal function. (/ 2.0) :: Float Float is the halving function. 13 / 85 </li> <li> Inx and Prex To use an inx operator in prex position, surrounded it in parentheses. For example, (+) 3 4 is equivalent to 3 + 4. Surround an ordinary function by back-quotes (not quotes!) to put it in inx position. E.g. 3 mod 4 is the same as mod 3 4. 14 / 85 </li> <li> Function Composition Functions composition: () :: (b c) (a b) (a c) (f g) x = f (g x) . E.g. another way to write quad: quad :: Int Int quad = square square . Some important properties: id f = f = f id, where id x = x. (f g) h = f (g h). 15 / 85 </li> <li> Guarded Equations Recall the denition: smaller :: Int Int Int smaller x y = if x y then x else y . We can also write: smaller :: Int Int Int smaller x y | x y = x | x &gt; y = y . Helpful when there are many choices: signum :: Int Int signum x | x &gt; 0 = 1 | x == 0 = 0 | x &lt; 0 = 1 . 16 / 85 </li> <li> Expressions Since functions are rst-class constructs, we can also construct functions in expressions. A expression denotes an anonymous function. x e: a function with argument x and body e. x y e abbreviates to x y e. In ASCII, we write as Examples: (x x x) 3 = 9. (x y x x + 2 y) 3 4 = 17. 17 / 85 </li> <li> Expressions Yet another way to dene smaller: smaller :: Int Int Int smaller = x y if x y then x else y . Why s? Sometimes we may want to quickly dene a function and use it only once. In fact, is a more primitive concept. 18 / 85 </li> <li> Local Denitions There are two ways to dene local bindings in Haskell. let-expression: f :: (Float, Float) Float f (x, y) = let a = (x + y)/2 b = (x + y)/3 in (a + 1) (b + 2) . where-clause: f :: Int Int Int f x y | x 10 = x + a | x &gt; 10 = x a where a = square (y + 1) . let can be used in expressions (e.g. 1 + (let..in..)), while where qualies multiple guarded equations. 19 / 85 </li> <li> Types The universe of values is partitioned into collections, called types. Some basic types: Int, Float, Bool, Char. . . Type constructors: functions, lists, trees . . . to be introduced later. Operations on values of a certain type might not make sense for other types. For example: square square 3. 20 / 85 </li> <li> Strong Typing Strong typing: the type of a well-formed expression can be deducted from the constituents of the expression. It helps you to detect errors. More importantly, programmers may consider the types for the values being dened before considering the denition themselves, leading to clear and well-structured programs. 21 / 85 </li> <li> Booleans The datatype Bool can be introduced with a datatype declaration: data Bool = False | True . (But you need not do so. The type Bool is already dened in the Haskell Prelude.) 22 / 85 </li> <li> Datatype Declaration In Haskell, a data declaration denes a new type. data Type = Con1 Type11 Type12 . . . | Con2 Type21 Type22 . . . | : The declaration above introduces a new type, Type, with several cases. Each case starts with a constructor, and several (zero or more) arguments (also types). Informally it means a value of type Type is either a Con1 with arguments Type11, Type12. . . , or a Con2 with arguments Type21, Type22. . . Types and constructors begin in capital letters. 23 / 85 </li> <li> Functions on Booleans Negation: not :: Bool Bool not False = True not True = False . Notice the denition by pattern matching. The denition has two cases, because Bool is dened by two cases. The shape of the function follows the shape of its argument. 24 / 85 </li> <li> Functions on Booleans Conjunction and disjunction: (&amp;&amp;), (||) :: Bool Bool Bool False &amp;&amp; x = False True &amp;&amp; x = x , False || x = x True || x = True . 25 / 85 </li> <li> Functions on Booleans Equality check: (==), (=) :: Bool Bool Bool x == y = (x &amp;&amp; y) || (not x &amp;&amp; not y) x = y = not (x == y) . = is a denition, while == is a function. = is written / = in ASCII. 26 / 85 </li> <li> Example leapyear :: Int Bool leapyear y = (y mod 4 == 0) &amp;&amp; (y mod 100 = 0 || y mod 400 == 0) . Note: y mod 100 could be written mod y 100. The backquotes turns an ordinary function to an inx operator. Its just personal preference whether to do so. 27 / 85 </li> <li> Characters You can think of Char as a big data denition: data Char = 'a' | 'b' | . . . with functions: ord :: Char Int chr :: Int Char . Characters are compared by their order: isDigit :: Char Bool isDigit x = '0' x &amp;&amp; x '9' . 28 / 85 </li> <li> Tuples The polymorphic type (a, b) is essentially the same as the following declaration: data Pair a b = MkPair a b . Or, had Haskell allow us to use symbols: data (a, b) = (a, b) . Two projections: fst :: (a, b) a fst (a, b) = a , snd :: (a, b) b snd (a, b) = b . 29 / 85 </li> <li> Either and Maybe Another standard datatype Either a b states that a value is either an a, or a b. data Either a b = Left a | Right b . Another standard datatype Either a b states that a value is either an a, or a b. data Maybe a = Just a | Nothing . We will see their usage in practicals. 30 / 85 </li> <li> Pattern Matching Previously, pattern matching only happens in the LHS of a function denition: f (x, y) = . . . g (Left x) = . . . g (Right x) = . . . You can also patten-match in a let-binding: f x = let (y, z) = . . . in . . . 31 / 85 </li> <li> case-expressions When there are more cases, you can use a case-expression: f x = case some expression of Left y . . . Right z . . . Both let and case can appear deep in expressions, be nested, etc. In fact, patterns are eventually translated to case expressions in the GHC core language. 32 / 85 </li> <li> What Are the Smaller Letters in Types? You might have noticed something odd in types of functions. For example fst may be applied to pairs of many dierent types: fst :: (Int, Char) Int, fst :: (Char, Int) Char, fst :: (Bool, Char) Bool . . . We give it a type that covers all the cases above: for all a and b, fst :: (a, b) a. The type can be instantiated to any of the types above, when fst is used. 33 / 85 </li> <li> Parametric Polymorphic Types Polymorphism: allowing one term to have many types. In the style we have seen above, the many types of fst are selected by assigning dierent types (Int, Bool, etc) to parameters (a and b). This style is thus called parametric polymorphism. Note that, in this style, the same function (the same algorithm) can be applied to many dierent types, because it does not matter to the algorithm what they are. E.g., fst does not need to know what a and b are in its input (a, b). 34 / 85 </li> <li> Equality Check You might have also noticed that we can check for equality of a number of types: (==) :: Char Char Bool, (==) :: Int Int Bool, (==) :: (Int, Char) (Int, Char) Bool, (==) :: [Int] [Int] Bool . . . It diers from the situation of fst: the algorithm for checking equality for each type is dierent. We just use the same name to refer to them, for convenience. 35 / 85 </li> <li> Equality Check Furthermore, not all types have (==) dened. For example, we cannot check whether two functions are equal. (==) is an overloaded name one name shared by many dierent denitions of equalities, for dierent types. It is some times called ad-hoc polymorphism. 36 / 85 </li> <li> Ad-Hoc Polymorphism Haskell deals with overloading by a general m...</li></ul>