Functional Programming and Haskell

advertisement
CS5205: Foundations in
Programming Languages
FP with Haskell
A pure lazy functional language that
embodies many innovative ideas
in language design.
CS5205
Haskell
1
Haskell
• Advanced programming language – reusable,
abstract, efficient, feature-rich.
• Functions and Expressions
• Values, Types and Lazy evaluation
• Products, Records and Algebraic Types
• Polymorphic Types & Type Classes
• Higher-Order Functions
• Infix operator and sections.
Reference --- A Gentle Introduction to Haskell 98
http://www.haskell.org/tutorial
CS5205
Haskell
2
Functions
• Function is a black box that converts input to
output. A basis of software component.
….
….
inputs
function
….
global
vars
function
….
output
output
impure
pure
CS5205
inputs
Haskell
3
Example of Functions
• Double a given input.
square :: Int -> Int
square x = x*x
• Conversion from fahrenheit to celcius
fahr_to_celcius :: Float -> Float
fahr_to_celcius temp = (temp – 32)/1.8
• A function with multiple results - quotient & remainder
divide ::
Int -> Int -> (Int,Int)
divide x y = (div x y, mod x y)
CS5205
Haskell
4
Expression-Oriented
• Instead of imperative commands/statements, the focus is
on expression.
• Instead of command/statement :
if e1 then stmt1 else stmt2
• We use conditional expressions :
if e1 then e2 else e3
CS5205
Haskell
5
Expression-Oriented
• An example function:
fact
fact n
:: Integer -> Integer
= if n=0 then 1
else n * fact (n-1)
• Can use pattern-matching instead of conditional
fact 0
fact n
= 1
= n * fact (n-1)
• Alternatively:
fact n
CS5205
= case n of
0 -> 1
a -> a * fact (a-1)
Haskell
6
Conditional  Case Construct
• Conditional;
if e1 then e2 else e3
• Can be translated to
case e1 of
True -> e2
False -> e3
• Case also works over data structures (without any extra
primitives)
length xs = case xs of
[] -> 0;
y:ys -> 1+(length ys)
Locally bound variables
CS5205
Haskell
7
Lexical Scoping
• Local variables can be created by let construct to give
nested scope for the name space. Example:
let y
= a+b
f x = (x+y)/y
in f c + f d
• For scope bindings over guarded expressions, we
require a where construct instead:
f x y
| x>z = …
| y==z = …
| y<z = ….
where z=x*x
CS5205
Haskell
8
Layout Rule
• Haskell uses two dimensional syntax that relies on
declarations being “lined-up columnwise”
let y
= a+b
f x = (x+y)/y
in f c + f d
is being parsed as:
let
{ y
= a+b
; f x = (x+y)/y }
in f c + f d
• Rule : Next character after keywords where/let/of/do
determines the starting columns for declarations.
Starting after this point continues a declaration, while
starting before this point terminates a declaration.
CS5205
Haskell
9
Expression Evaluation
• Expression can be computed (or evaluated) so that it is
reduced to a value. This can be represented as:
e
 ..
 v
• We can abbreviate above as:
e
*
v
• A concrete example of this is:
inc (inc 3)  inc (4)  5
• Type preservation theorem says that:
if e :: t Æ e  v , it follows that v :: t
CS5205
Haskell
10
Values and Types
• As a purely functional language, all computations are
done via evaluating expressions (syntactic sugar) to
yield values (normal forms as answers).
• Each expression has a type which denotes the set of
possible outcomes.
• v :: t can be read as value v has type t.
• Examples of typings, associating a value with its
corresponding type are:
5
'a'
[1,2,3]
('b', 4)
“cs5”
CS5205
::
::
::
::
::
Integer
Char
[Integer]
(Char, Integer)
String (same as [Char])
Haskell
11
Built-In Types
• They are not special:
data Char
data Int
data Integer
= ‘a’ | ‘b’ | …
= -65532 | … | -1 | 0 | 1 | …. | 65532
= … | -2 | -1 | 0 | 1 | 2 | …
• Tuples are also built-in.
data (a,b)
= M2(a,b)
data (a,b,c)
= M3(a,b,c)
data (a,b.c.d) = M4(a,b,c,d)
• List type uses an infix operator:
data [a]
[1,2,3]
CS5205
= []
is short hand for
Haskell
|
a : [a]
1 : (2 : (3 : []))
12
User-Defined Algebraic Types
• Can describe enumerations:
data Bool
data Color
= False | True
= Red | Green | Blue | Violet
• Can also describe a tuple
data Pair
= P2 Integer Integer
data Point a = Pt a a
type variable
• Pt is a data constructor with type a -> a -> Point a
Pt 2.0 3.1
Pt ‘a’ ‘b’
Pt True False
CS5205
:: Point Float
:: Point Char
:: Point Bool
Haskell
13
Recursive Types
• Some types may be recursive:
data Tree a = Leaf a | Branch (Tree a) (Tree a)
• Two data constructors:
Leaf
:: a -> Tree a
Branch :: Tree a -> Tree a -> Tree a
• An example function over recursive types:
fringe :: Tree a -> [a]
fringe (Leaf x)
= [x]
fringe (Branch left right) = (fringe left) ++
(fringe right)
CS5205
Haskell
14
Polymorphic Types
• Support types that are universally quantified in some
way over all types.
• 8 c. [c] denotes a family of types, for every type c, the type
of lists of c.
• Covers [Integer],
[Char], [Integer->Integer],
etc.
• Polymorphism help support reusable code, e.g
length
:: 8 a. [a] -> Integer
length []
= 0
length (x:xs) = 1 + length xs
CS5205
Haskell
15
Polymorphic Types
• This polymorphic function can be used on list of any type..
length [1,2,3]
length [‘a’, ’b’, ‘c’]
length [[1],[],[3]]
)
)
)
2
3
3
• More examples :
head
head (x:xs)
:: [a] -> a
= x
tail
tail (x:xs)
:: [a] -> [a]
= xs
• Note that head/tail are partial functions, while length is
a total function?
CS5205
Haskell
16
Principal Types
• Some types are more general than others:
[Char]
<:
8 a. [a]
<:
8 a. a
• An expression’s principal type is the least general type
that contains all instances of the expression.
• For example, the principal type of head function is
[a]->a, while [b] -> a, b -> a, a are correct but
too general but [Integer] -> Integer is too
specific.
• Principal type can help supports software reusability
with accurate type information.
CS5205
Haskell
17
Type Synonyms
• It is possible to provide new names for commonly used
types
type
type
type
data
String
Person
Name
Address
=
=
=
=
[Char]
(Name, Address)
String
None | Addr String
• Type synonyms do not define new types, but simply
give new names for existing types.
type AssocList a b = [(a,b)]
• Their use can improve code readability.
CS5205
Haskell
18
Type Classes and Overloading
• Parametric polymorphism works for all types. However,
ad-hoc polymorphism works for a class of types and can
systematically support overloading.
• For example, what is the type for (+) operator?
(+) :: a -> a -> a
(+) :: Int -> Int -> Int
(+) :: Float -> Float -> Float
• What about methods built from (+)?
double x = x+x
• Better solution is to use a type class for numerics!
(+) :: Num a => a -> a -> a
double :: Num a => a -> a
CS5205
Haskell
19
Equality Class
• Similar issue with equality. Though it looks like:
(==) :: a -> a -> Bool
• Equality can be tested for data structures but not
functions!
• Solution is to define a type class that supports the
equality method, as follows:
class Eq a where
(==),(/=) :: a -> a -> Bool
x /= y
= not (x==y)
default definition
• Then:
CS5205
(==) :: Eq a => a -> a -> Bool
Haskell
20
Members of Eq Type Class
• The class is open and can be extended, as follows:
instance Eq Integer where
x == y
= x ‘integerEq’ y
instance Eq Float where
x == y
= x ‘FloatEq’ y
• Recursive type can also be handled but elements may
need to be given type qualifiers.
instance (Eq a) => Eq (Tree a) where
Leaf a == Leaf b = a==b
(Branch l1 r1) == (Branch l2 r2)
= (l1==l2) && (r1==r2)
_ == _
= False
CS5205
Haskell
21
More Members of Eq Type Class
• Similarly, we may use structural equality for List:
instance (Eq a) => Eq
[] == []
(x:xs) == (y:ys)
_ == _
([a]) where
= True
= (x==y) && (xs==ys)
= False
• One use of Eq class is:
elem :: (Eq a) => a -> [a] -> Bool
x ‘elem’ []
= False
x ‘elem’ (y:ys)
= (x==y) || (x ‘elem’ ys)
• Exercise : define Set as an instance of the Eq class.
CS5205
Haskell
22
Numeric Class
• There is a hierarchy of numeric classes. Most basic is:
class Num a where
(+),(-),(*) :: a -> a -> a
negate,abs,signum :: a -> a
fromInteger :: Integer -> a
x – y
= x + (negate y)
negate x = 0 – x
instance
…
instance
…
instance
…
instance
…
CS5205
Num Int where
Num Integer where
Num Float where
Num Double where
Haskell
23
Functions and its Type
• Method to increment its input
inc x
=
x+1
• Or through lambda expression (anonymous functions)
(\ x -> x+1)
• They can also be given suitable function typing:
inc
(\x -> x+1)
:: Num a => a -> a
:: Num a => a -> a
• Types can be user-supplied or inferred.
CS5205
Haskell
24
Functions and its Type
• Some examples
(\x -> x+1) 3.2
(\x -> x+1) 3


• User can restrict the type, e.g.
inc
:: Int -> Int
• In that case, some examples may be wrongly typed.
inc 3.2
inc 3
CS5205


Haskell
25
Function Abstraction
• Function abstraction is the ability to convert any
expression into a function that is evaluated at a later
time.
<Expr>
p = \ () -> Expr
time
p ()
time
Normal Execution
CS5205
Delayed Execution
Haskell
26
Higher-Order Functions
• Higher-order programming treats functions as first-class,
allowing them to be passed as parameters, returned as
results or stored into data structures.
• This concept supports generic coding, and allows
programming to be carried out at a more abstract level.
• Genericity can be applied to a function by letting
specific operation/value in the function body to become
parameters.
CS5205
Haskell
27
Functions
• Functions can be written in two main ways:
add
add x y
:: Integer -> Integer -> Integer
= x+y
add2
add2(x,y)
:: (Integer,Integer) -> Integer
= x+y
• The first version allows a function to be returned as
result after applying a single argument.
inc
inc
:: Integer -> Integer
= add 1
• The second version needs all arguments. Same effect
requires a lambda abstraction:
inc
CS5205
= \ x -> add2(x,1)
Haskell
28
Functions
• Functions can also be passed as parameters. Example:
map
:: (a->b) -> [a] -> [b]
map f []
= []
map f (x:xs) = (f x) : (map f xs)
• Such higher-order function aids code reuse.
map (add 1) [1, 2, 3]
map add [1, 2, 3]
) [2, 3, 4]
) [add 1, add 2, add 3]
• Alternative ways of defining functions:
add
add
CS5205
= \ x -> \ y -> x+y
= \ x y -> x+y
Haskell
29
Genericity
• Replace specific entities (0 and +) by parameters.
sumList ls =
case ls of
[]
-> 0
x:xs -> x+(sumList xs)
foldr f u ls =
case ls of
[]
-> u
x:xs -> f x (foldr f u xs)
CS5205
Haskell
30
Polymorphic, Higher-Order Types
sumList :: [Int] -> Int
sumList :: Num a => [a] -> a
foldr :: (a -> b -> b) -> b
-> [a] -> b
CS5205
Haskell
31
Instantiating Generic Functions
sumL2 :: Num a => [a] -> a
sumL2 ls = foldr (+) 0 ls
sumL2 [1, 2, 3]
)
sumL2 [1.1, 3, 2.3]
)
CS5205
Haskell
32
Instantiating Generic Functions
prodL :: Num a => [a] -> a
prodL ls = foldr (*) 1 ls
prodL [1, 2, 5]
)
prodL [1.1, 3, 2.3]
)
CS5205
Haskell
33
Instantiating Generic Functions
• Can you express map in terms of foldr?
map :: (a -> b) -> [a] -> [b]
map f []
= []
map f (x:xs) = (f x) : (map f xs)
map f xs
CS5205
= foldr … … … xs
Haskell
34
Instantiating Generic Functions
• Filtering a list of elements with a predicate.
filter :: (a -> Bool) -> [a] -> [a]
filter f []
= []
filter f (x:xs) =
if (f x) then x : (filter f xs)
else x : filter f xs
• Can we express filter in terms of foldr?
filter f xs
CS5205
= foldr … … … xs
Haskell
35
Pipe/Compose
compose :: (b -> c) -> (a -> b)
-> a -> c
compose f g
= \ x -> f (g x)
g | f
= compose f g
• Similar to Unix pipe command:
cmd1
CS5205
|
cmd2
Haskell
36
Iterator Construct
for :: Int -> Int -> (Int -> a -> a)
-> a -> a
for i j f a
=
if i>j then a
else f (i+1) j (f i a)
• In Haskell, type class help give a more generic type:
for :: Num b, Ord b => b -> b ->
(b -> a -> a) -> a -> a
CS5205
Haskell
37
Right Folding
foldr f u [x1,x2,..,xn]
 f x1 (foldr f u [x2 ..xn])
 f x1 (f x2 (fold f u [x3..xn]))
 f x1 (f x2 (… (fold f u [xn]) …))
 f x1 (f x2 (… (f xn u) …)))
associate to
right
CS5205
Haskell
38
Left Folding – Tail Recursion
• Accumulate result in a parameter:
foldl f u ls =
case ls of
[]
-> u
x:xs -> foldl f (f u x) xs
based on accumulation
• What is the type of foldl?
• Can we compute factorial using it?
CS5205
Haskell
39
Left Folding
foldl f u [x1,x2,..,xn]
 foldl f (f u x1) [x2 ..xn]
 foldl f (f (f u x1) x2) [x3..xn]))
 foldl f (f … (f (f u x1) x2)… xn) []
 f (… (f (f u x1) x2) …) xn
left is here!
CS5205
Haskell
40
Instance of Left Folding
• Summing a list by accumulation.
sumT acc ls =
case ls of
[]
-> 0
x:xs -> sumT (x+acc) xs
sumList ls = sumT 0 ls
sumT acc ls = foldl (+) acc ls
CS5205
Haskell
41
Infix Operator
• Infix operators are distinguished entirely by symbols:
(++)
[] ++ ys
(x:xs) ++ ys
:: [a]-> [a] -> [a]
= ys
= x : (xs ++ ys)
• Another example is function composition.
(.)
f . g
:: (b->c) -> (a->b) -> (a->c)
= \ x -> f (g x)
• Infix operator can be coerced to prefix by bracketing,
e.g. map (+) [1,2,3]
• Alphanumeric named function can be made infix too,
e.g. a ‘add’ b
x ‘elem’ xs
CS5205
Haskell
42
Sections
• Infix operators can also be partially applied:
(x+)
(+y)
(+)
´
´
´
\y -> x+y
\x -> x+y
\x y -> x+y
=
=
(+ 1)
(+)
• Other examples:.
inc
add
• These are syntactic sugar for programming
conveniences.
CS5205
Haskell
43
Download