Functional Programming (adapted from Dave Sands) Why learn functional programming? * Functional programming will make you think differently about programming – mainstream programming is all about state and how to transform state – functional programming is all about values and how to construct values using functions *  Whether you use it later or not, it will make you a better programmer Why Haskell? • Haskell is a very high-level language (many details taken care of automatically). • Haskell is expressive and concise (can achieve a lot with a little effort). • Haskell is good at handling complex data and combining components. • Haskell is a high-productivity language (prioritise programmer-time over computer-time) >>> Haskell Demo * Let's say we want to buy a game in the USA and we have to convert its price from USD to EUR * A ::definition:: gives a name to a value * Names are case-sensitive, must start with lowercase letter * Definitions are put in a text file ending in .hs Examples.hs: %%%%%%%%%%%%%%%%%%%% dollarRate = 1.3671 %%%%%%%%%%%%%%%%%%%% >>> Using the definition * Starting the GHCi Haskell interpreter (loads the file) shell> ghci GHCi, version 7.4.2: http://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Prelude> :l Examples.hs * Use the definition Main> dollarRate Main> 53 * dollarRate >>> A Function to convert Euros to USD Examples.hs %%%%%%%%%%%%%%%%%%%% -- convert euros to USD usd x = x * dollarRate %%%%%%%%%%%%%%%%%%%% * first line: comment * ::usd:: function name (defined) * ::x:: argument name (defined) * ::x * dollarRate:: expression to compute the result >>> Using the function * load into GHCi Main> usd 1 Main> usd 73 >>> Converting Back *Main> euro (usd 73) 73.0 *Main> euro (usd 1) 1.0 *Main> usd (euro 100) 100.0 >>> Automated Testing * Define a function to perform the test for us! %%%%%%%%%%%%%%%%%%%% prop_EuroUSD x = euro (usd x) == x %%%%%%%%%%%%%%%%%%%% * == is the equality operator *Main> prop_EuroUSD 79 True *Main> prop_EuroUSD 1 True >>> Writing Properties •  Convention: functions names beginning with ”prop_” are properties we expect to be True •  Writing properties in files – Tells us how functions should behave – Tells us what has been tested – Lets us repeat tests after changing a definition >>> Automatic Testing •  Testing accounts for more than half the cost of a software project •  We employ a widely used Haskell library for automatic random testing %%%%%%%%%%%%%%%%%%%% import Test.QuickCheck %%%%%%%%%%%%%%%%%%%% * add imports in file before all definitions >>> Running Tests *Main> quickCheck prop_EuroUSD *** Failed! Falsifiable (after 10 tests and 1 shrink): 7.0 * Runs 100 randomly chosen tests * Result: it's wrong! * Property fails for input 7.0 *Main> usd 7 9.5697 *Main> euro 9.5697 6.999999999999999 >>> The Problem: Floating Point Arithmetic •  There is a very tiny difference between the initial and final values *Main> euro (usd 7) - 7 -8.881784197001252e-16 •  Calculations are only performed to about 15 significant figures •  The property is wrong! >>> Fixing the Problem •  NEVER use equality with floating point numbers! •  The result should be nearly the same •  The difference should be small – smaller than 10E-15 >>> Comparing Values *Main> 2<3 True *Main> 3<2 False >>> Defining "Nearly Equal" •  We can define new operators with names made up of symbols %%%%%%%%%%%%%%%%%%%% x ~== y = x - y < 10e-15 %%%%%%%%%%%%%%%%%%%% *Main> 3 ~== 3.0000001 True *Main> 3 ~== 4 True >>> Fixing the Definition * abs computes the absolute value *Main> abs 3 3 *Main> abs (-3) 3 %%%%%%%%%%%%%%%%%%%% x ~== y = abs (x - y) < 10e-15 %%%%%%%%%%%%%%%%%%%% *Main> 3 ~== 4 False >>> Fixing the Property %%%%%%%%%%%%%%%%%%%% prop_EuroUSD' = euro (usd x) ~== x %%%%%%%%%%%%%%%%%%%% *Main> prop_EuroUSD' 3 True *Main> prop_EuroUSD' 56 True *Main> prop_EuroUSD' 77 True ... but it's still wrong because the approximate equality must take the absolute size of the argument into account! An improved definition that survives 100 tests: %%%%%%%%%%%%%%%%%%%% x ~== y = abs (x - y) < 10e-15 * abs x %%%%%%%%%%%%%%%%%%%% >>> Name the Price •  Let’s define a name for the price of the game we want %%%%%%%%%%%%%%%%%%%% price = 79 %%%%%%%%%%%%%%%%%%%% *Main> euro price :57:6: Couldn't match expected type `Double' with actual type `Integer' In the first argument of `euro', namely `price' In the expression: euro price In an equation for `it': it = euro price >>> Every Value has a Type •  The :i command prints information about a name *Main> :i price price :: Integer -- Defined at ... *Main> :i dollarRate dollarRate :: Double -- Defined at ... >>> More Types *Main> :i True data Bool = ... | True -- Defined in `GHC.Types' *Main> :i False data Bool = False | ... -- Defined in `GHC.Types' *Main> :i euro euro :: Double -> Double -- Defined at... *Main> :i prop_EuroUSD' prop_EuroUSD' :: Double -> Bool -- Defined at... * True and False are ::data constructors:: >>> Types Matter •  Types determine how computations are performed •  A type annotation specifies which type to use *Main> 123456789*123456789 :: Double 1.524157875019052e16 *Main> 123456789*123456789 :: Integer 15241578750190521 •  Double: double precision floating point numbers •  Integer: exact computation •  GHCi must know the type of each expression before computing it. >>> Type Checking •  Infers (works out) the type of every expression •  Checks that all types match – before running the program >>> Our Example *Main> :i price price :: Integer -- Defined at... *Main> :i euro euro :: Double -> Double -- Defined at... *Main> euro price :70:6: Couldn't match expected type `Double' with actual type `Integer' In the first argument of `euro', namely `price' In the expression: euro price In an equation for `it': it = euro price >>> Why did it work before? •  Numeric literals are ::overloaded:: •  Giving the number a name fixes its type *Main> euro 79 57.78655548240802 *Main> 79 :: Integer 79 *Main> 79 :: Double 79.0 *Main> price :: Integer 79 *Main> price :: Double :76:1: Couldn't match expected type `Double' with actual type `Integer' In the expression: price :: Double In an equation for `it': it = price :: Double >>> Fixing the Problem •  A definition can be given a type signature which specifies its type %%%%%%%%%%%%%%%%%%%% -- price of the game in USD price' :: Double price' = 79 %%%%%%%%%%%%%%%%%%%% *Main> :i price' price' :: Double -- Defined at... *Main> euro price' 57.78655548240802 >>> Always Specify Type Signatures! •  They help the reader (and you) understand the program •  They serve as a starting point for implementation •  The type checker can find your errors more easily, by checking that definitions have the types you say •  Type error messages will be easier to understand •  Sometimes they are necessary (as for price) >>> Example with Type Signatures %%%%%%%%%%%%%%%%%%%% dollarRate :: Double dollarRate = 1.3671 usd, euro :: Double -> Double usd x = x*dollarRate euro x = x/dollarRate prop_EuroUSD' :: Double -> Bool prop_EuroUSD' x = euro (usd x) ~== x %%%%%%%%%%%%%%%%%%%% >>> Function Definition by Cases and Recursion • Example: Absolute Value •  Find the absolute value of a number – If x is positive, result is x – If x is negative, result is -x %%%%%%%%%%%%%%%%%%%% -- returns the absolute value of x absolute :: Integer -> Integer absolute x | x >= 0 = x absolute x | x < 0 = - x %%%%%%%%%%%%%%%%%%%% >>> Alternative Notation %%%%%%%%%%%%%%%%%%%% absolute x | x >= 0 = x | x < 0 = -x %%%%%%%%%%%%%%%%%%%% ... or using if-then-else %%%%%%%%%%%%%%%%%%%% absolute x = if x >= 0 then x else -x %%%%%%%%%%%%%%%%%%%% >>> Recursion •  First example of a recursive function – Compute x^n (without using the built-in x^n) %%%%%%%%%%%%%%%%%%%% -- compute x to n-th power power x 0 = 1 power x n | n > 0 = x * power x (n - 1) %%%%%%%%%%%%%%%%%%%% >>> Recursion •  Reduce a problem (e.g. power x n) to a smaller problem of the same kind •  So that we eventually reach a ”smallest” base case •  Solve base case separately •  Build up solutions from smaller solutions >>> Example: Counting Intersections •  Consider n non-parallel lines in the plane. • How many intersections (at most)? >>> The Solution •  Always pick the base case as simple as possible! %%%%%%%%%%%%%%%%%%%% -- max number of intersections of n lines intersect :: Integer -> Integer intersect 0 = 0 intersect n | n > 0 = intersect (n - 1) + n - 1 %%%%%%%%%%%%%%%%%%%% Summary: - A *definition* gives a name to a value - Names are case-sensitive, must start with lowercase letter - Definitions are put in a text file ending in .hs - Comments start with "--" and extend to the end of the line - Function definitions have the form "f x1 x2 ... = e" where - f is the name of the function - x1, x2, ... are the names of the formal parameters - e is the body of the function, an expression to compute the result - Operators - arithmetic: + - * / - comparison: == /= < > etc - Automated Testing with QuickCheck - Define a property as a function "prop_..." of type T -> Bool - import Testing.QuickCheck -- in the source file - Test by executing "quickCheck prop_..." - Floating point arithmetic - Do not test floating point numbers for equality! - Approximately 15 significant decimal digits - Test equality with relative check < 10E-15 - Types - Every value has a type - Numeric literals are overloaded (can be, e.g., Integer or Double) - Naming a value fixes its type (according to Haskell's rules) - Argument type of function and actual type of argument must match - Type Double: double precision floating point numbers - Type Integer: exact computation - Type signature specifies the desired type (overrides Haskell's rules) - Always specify type signatures! - Function definition by cases and recursion - Paraphrasing the mathematical definition